Limited Time Event
Invite 5 friends, Earn Steam Keys!

All rewards feature "Very Positive" user ratings and values ranging from $5 to $40. (And it's repeatable!)

View your referral link and progress in Profile Settings

Transparency Report

We believe the community deserves to know exactly how SteamDrops works under the hood — from how winners are picked to how we detect and remove bad actors.

Provably Fair Open Process Privacy First

Winner Selection Algorithm

Every giveaway uses the same deterministic, server-side random selection process. There is no manual override, no weighting by points balance, and no preferential treatment — every entry has an equal chance.

1. Giveaway closes
The end timestamp is reached. No new entries are accepted from this point. The entry pool is locked.
2. Minimum entry check
If fewer than 5 entries exist the giveaway is automatically cancelled and all entry points are refunded in full.
3. Eligible entries loaded
All entries are fetched from the database. Any entry from a currently banned user is excluded before the draw.
4. Cryptographic shuffle
PHP's random_int() — a CSPRNG (cryptographically secure pseudo-random number generator) — is used to select a winner index. This is the same source of randomness used in cryptography.
5. Winner recorded
The selected Steam ID is written atomically to the database inside a transaction. The encrypted key is associated with the winner's account.
6. Notification dispatched
The winner receives an on-site notification and (if subscribed) a Web Push notification. The key remains hidden until they manually reveal it.
Rerolls: If a winner does not reveal their key within 7 days, the system automatically selects a new winner from the remaining eligible entries using the same CSPRNG process. The original winner loses their claim.

Probability & Odds

Your odds are simple and transparent. Every entry carries exactly equal weight — there is no way to buy extra tickets or boost your probability beyond entering once.

Formula:   Win Probability = 1 ÷ Total Entries

Live Example — 100 entries

Each bar represents one entrant's share of the total pool (100 entries)
You
1.0%
Every other
1.0%

All 100 bars are identical — no entry has any advantage over another.

Odds at different pool sizes

Total entries Your chance 1 in X Multiple copies (5)
5 (minimum)20.00%1 in 5100% (all 5 won)
1010.00%1 in 1050.00%
254.00%1 in 2520.00%
502.00%1 in 5010.00%
1001.00%1 in 1005.00%
5000.20%1 in 5001.00%
Multiple copies: For giveaways with more than one copy, winners are drawn sequentially without replacement — each winner is removed from the pool before the next draw. This guarantees no duplicate winners.

Winner Selection — Source Code

The exact logic that picks every winner on this platform. This is the production code, unmodified.

src/Services/WinnerService.php — pickWinners()
/**
 * Selects winner(s) for all giveaways that have ended and are still pending.
 * Called by the cron scheduler every minute.
 */
public function pickWinners(): void
{
    // Fetch all giveaways that have ended but have no winner yet
    $giveaways = Giveaway::getEndedPending();

    foreach ($giveaways as $giveaway)
    {
        $this->processGiveaway($giveaway);
    }
}

private function processGiveaway(array $giveaway): void
{
    $id      = (int)$giveaway['id'];
    $copies  = (int)$giveaway['copies'];

    // Load all eligible entries — excludes currently banned users
    $entries = Entry::getEligibleForGiveaway($id);

    // Minimum entry guard — cancel and refund if under threshold
    if (count($entries) < self::MIN_ENTRIES)
    {
        $this->cancelAndRefund($giveaway);
        return;
    }

    $db = Database::getInstance();
    $db->beginTransaction();

    try
    {
        $winners  = [];
        $pool     = $entries; // mutable pool — winners are removed after each draw

        for ($i = 0; $i < $copies; $i++)
        {
            if (empty($pool)) break;

            // CSPRNG — cryptographically secure random integer
            // random_int() uses /dev/urandom on Linux (same source as OpenSSL)
            $index  = random_int(0, count($pool) - 1);
            $winner = $pool[$index];

            // Remove winner from pool to prevent duplicate wins
            array_splice($pool, $index, 1);

            // Record the win — links encrypted key to winner's Steam ID
            GiveawayKey::assignWinner($winner['user_id'], $id);
            $winners[] = $winner['user_id'];
        }

        Giveaway::markCompleted($id);
        $db->commit();

        // Dispatch notifications outside the transaction
        foreach ($winners as $winnerId)
        {
            Notification::create($winnerId, 'win', [
                'giveaway_id' => $id,
                'title'       => $giveaway['game_name'],
            ]);
        }
    }
    catch (\Throwable $e)
    {
        $db->rollback();
        throw $e;
    }
}
src/Models/Entry.php — getEligibleForGiveaway()
/**
 * Returns all entries for a giveaway, excluding banned users.
 * The result set is the exact pool fed into random_int().
 */
public static function getEligibleForGiveaway(int $giveawayId): array
{
    return self::getDb()->fetchAll(
        "SELECT e.user_id, e.created_at
         FROM entries e
         JOIN users u ON u.steam_id = e.user_id
         WHERE e.giveaway_id = ?
           AND (u.ban_expires_at IS NULL OR u.ban_expires_at < UNIX_TIMESTAMP())
           AND u.is_deleted = 0
         ORDER BY e.created_at ASC",
        [$giveawayId]
    );
}
Why random_int() and not array_rand()?
PHP's array_rand() uses the Mersenne Twister PRNG which is not cryptographically secure — its output can be predicted from previous outputs with enough samples. random_int() draws directly from the OS entropy pool (/dev/urandom) and is the same source used by TLS and password hashing. We use it here so that no statistical analysis can predict or influence winner selection.

Bot & Alt Account Detection

We operate a continuous, multi-layered detection system that runs in the background on every account. Suspicious patterns are surfaced to staff for manual review — no account is ever banned by automation alone.

Behavioural Analysis
We analyse patterns in how accounts interact with the platform over time. Automated behaviour produces distinct patterns that differ measurably from genuine human use.
Network Relationships
Connections between accounts are mapped and analysed. Clusters of accounts that exhibit correlated behaviour are investigated as a group rather than in isolation.
Statistical Similarity
Quantitative similarity metrics are computed between account activity histories. Accounts with unusually high overlap are flagged for review regardless of how they are registered.
Composite Risk Scoring
Multiple independent signals are combined into a per-account risk score. No single signal triggers action — we require convergence across several dimensions before escalating to review.
Why we don't publish the specifics: Detailed detection logic is intentionally kept private. Publishing thresholds, signal weights, or code would allow bad actors to calibrate their behaviour to stay just below detection. The goal of this section is to confirm that detection exists and is active — not to document it.
Manual review always applies. Every flagged account is reviewed by a staff member before any action is taken. Automated scoring surfaces candidates — humans make the final call.

Referral Abuse Detection

The referral system rewards users who genuinely bring new members to the community. We actively monitor for coordinated attempts to exploit it.

What constitutes referral abuse

  • Creating or controlling multiple accounts to claim referral rewards fraudulently.
  • Coordinating with others to manipulate giveaway entry pools as a group.
  • Using automation or scripts to generate fake account activity.
  • Any attempt to inflate one account's winning chances at the expense of legitimate users.
Consequences: Accounts confirmed as part of an abuse network are permanently banned — including the root account and every account in the network. All entries made by those accounts are voided, affected giveaways are redrawn with a clean pool, and any referral rewards earned fraudulently are reversed.
Detection methods are not published. We confirm that active monitoring exists and that action is taken — we do not document how, as doing so would only assist those attempting to evade it.
Community reports help. If you notice suspicious patterns, report it through the support system. Tips help us act faster.

Ban Process

No account is banned automatically. Every ban is issued by a staff member after manual review of the evidence. The process is designed to be auditable and reversible.

Detection signal fires
Automated tooling flags the account(s) and surfaces them in the moderation queue with a risk score and breakdown.
Manual review
A staff member opens the referral tree inspector, reviews entry speed data, overlap scores, Steam profile age, and any community reports.
Ban issued with reason
If the evidence is clear, the account is banned with a stated reason (e.g. Botting / Alt Accounts). The reason is stored in the database against that account's record.
Entries voided
Any active giveaway entries from the banned accounts are removed and the affected giveaways reprocess their winner selection with a clean pool.
Public record updated
The ban appears on the public ban list with the reason and duration. Permanent bans show no expiry date.
Appeals: If you believe your account was banned in error, open a support ticket. Bans are reviewed case-by-case and reversed if the evidence does not support them.

Data We Collect

We collect only what is necessary to run the platform. Nothing is sold, nothing is shared with third parties, and no advertising trackers are loaded.

Data Source Stored? Purpose
Steam ID (64-bit) Steam OpenID Yes Account identity
Username & Avatar Steam API Yes Display on site
Library Game IDs Steam API (on registration) Yes Eligibility check and Filtering owned games on giveaways
Email address No We never ask for it
Password No Login is Steam OpenID only
IP address No Server logs route to /dev/null
Session token Server-generated Yes Keep you logged in
Referral origin Referral cookie Yes Referral credit — stored as Steam ID
Cookies used: One authentication session cookie (auth) and one optional CSRF token (XSRF-TOKEN). No analytics cookies, no advertising cookies, no third-party trackers.

Steam Key Encryption

Every Steam key stored in our database is encrypted. Even in the event of a full database compromise, raw keys cannot be extracted without the encryption key — which is never stored in the database.

AES-256
Encryption standard
CBC
Mode of operation
1
User who can decrypt
src/Core/Encryption.php
class Encryption
{
    private const CIPHER = 'AES-256-CBC';

    /**
     * Encrypt a plaintext string.
     * A unique random IV is generated per encryption — the same key
     * will never produce the same ciphertext twice for the same input.
     */
    public static function encrypt(string $plaintext): string
    {
        $key = base64_decode(Config::get('APP_ENCRYPTION_KEY'));
        $iv  = random_bytes(openssl_cipher_iv_length(self::CIPHER)); // CSPRNG IV

        $ciphertext = openssl_encrypt($plaintext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv);

        // IV is prepended to ciphertext and base64-encoded for storage
        // Without the APP_ENCRYPTION_KEY env var, the IV is useless
        return base64_encode($iv . $ciphertext);
    }

    /**
     * Decrypt — only called at the moment a winner clicks "Reveal Key".
     * The decrypted key is returned to the winner's browser over TLS
     * and is never stored in plaintext anywhere.
     */
    public static function decrypt(string $encoded): string
    {
        $key    = base64_decode(Config::get('APP_ENCRYPTION_KEY'));
        $data   = base64_decode($encoded);
        $ivLen  = openssl_cipher_iv_length(self::CIPHER);
        $iv     = substr($data, 0, $ivLen);
        $cipher = substr($data, $ivLen);

        return openssl_decrypt($cipher, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
    }
}
  • The encryption key lives only in the .env file on the server — never in the database.
  • Keys are decrypted on-demand only when the winner clicks "Reveal Key" — no background process ever holds plaintext keys in memory longer than one request.
  • The decrypted key is transmitted to the winner over TLS (HTTPS) enforced by Cloudflare.
  • Only the winning Steam account can trigger decryption — the key ID is validated against the session's Steam ID before any decryption occurs.

Public Ban List

All active bans are visible to the public. We believe transparency deters abuse and reassures legitimate users that their competition is clean.

The full list is available at /bans — updated in real time, cached for performance.

What the ban list shows

  • Username & Steam profile link of the banned account.
  • Ban reason — e.g. Botting / Alt Accounts, Fraud, Harassment.
  • Date banned — when the ban was issued.
  • Expiry — exact date, or "Permanent" for indefinite bans.
Why publish it? Every bot account in the pool reduces everyone else's odds. Knowing they are removed — and seeing the evidence — gives the community confidence that giveaways are fair. Transparency is the cheapest form of accountability.
Note on cache: The ban list is cached for up to 2 hours for performance. A ban issued in the last 2 hours may not appear immediately — but the account's entries are voided instantly at the time of the ban, regardless of cache state.
Return to Homepage