Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:115097 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 36543 invoked from network); 24 Jun 2021 07:10:38 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 24 Jun 2021 07:10:38 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id A77851804F3 for ; Thu, 24 Jun 2021 00:29:20 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-lf1-f51.google.com (mail-lf1-f51.google.com [209.85.167.51]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 24 Jun 2021 00:29:20 -0700 (PDT) Received: by mail-lf1-f51.google.com with SMTP id h15so8510655lfv.12 for ; Thu, 24 Jun 2021 00:29:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paragonie-com.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=cAPrUMKUHcCFnJqd2zCkrL2b3cEvBTPz7oLXjfXxFIc=; b=nKNosj+Iy5VRWR1D+bjB1VHQ40zOn/OYay+TY3eGQEcOz4yHEM/BJu/EQ2r2n8tuz9 /iL32uSYUnW/c6+TaoyggZKfzgMpR1bKJRPxL+5w/46tDYzAEVlaWWF+KuIba2+TysZw wxQVgfTm9CtzKHkPNuzt6Q4grrnozzgb6q/Bi1+3EHvZmdGX36BsPx4GRxtmoC93AbjZ iyACjj/WiTpKzPhDqrRCVJTEkklskmgC2uIgcr/3/B2w58yRltGviU63b9Vc8buKcwF5 UJWdjVKpOHbAV9HzLDzqsZ+yfAnztLt8vu8LD54JFImyasJ3/Hgh04z4NoVfOegPPibc BSPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=cAPrUMKUHcCFnJqd2zCkrL2b3cEvBTPz7oLXjfXxFIc=; b=qT9GU/NDp14JKmBwDyAo4kjIKss/9Uzn+09AjPmLTqXS2YO+QY9/GV2JZ9KWt82w1Q /vxlWb+kmE2yYZw9MilftDd6jGRZmn9dVvrpeN1nyHhpq02jI8mk0/MTxkISTI/kx8FT awHcQSAmv7/YflzHB3Sxrg3j135axrOQPWGKhdpgY6g9mjZsn1foYW79ZH82DYOJGuAm Fu0pdR2zEIGS1yQjwEkDNlz4RfjrC8UGZLGeHcwgzEWf5ekJP0uWGIex/s4oEriiRlg3 gmTJJuHRQkwrV7MVaqrKqvGVfB5kNg1ZIsrd1VLc+J6Z/FlNuW6QFu70ZvyJYELcIuHb ZHJA== X-Gm-Message-State: AOAM533SZ6el5AS9vdy8Ly138oOS4S+EEScJE1mEO4UORHsj3uL2NB8B V5nbjfJWbQExwps6ULs6sIrlzE8rDGzcFJ70nQ5cCg== X-Google-Smtp-Source: ABdhPJyq4GDv1r8q/i/1qrikJ5ZEYrjyE1OX09A0JqVe3eTUQu37pNbJV9QLDIRE4FPyvALpSkJDqqzpkudcF+0eU6A= X-Received: by 2002:a05:6512:50b:: with SMTP id o11mr2809623lfb.257.1624519758397; Thu, 24 Jun 2021 00:29:18 -0700 (PDT) MIME-Version: 1.0 References: <03f7955c-69a8-4841-9245-449d7851e207@www.fastmail.com> <95D16F2E-E9DD-4964-A0E2-62E1FB0D976B@koalephant.com> In-Reply-To: <95D16F2E-E9DD-4964-A0E2-62E1FB0D976B@koalephant.com> Date: Thu, 24 Jun 2021 03:29:08 -0400 Message-ID: To: Stephen Reay Cc: Bruce Weirdan , Craig Francis , Larry Garfield , php internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] [RFC] Name issue - is_literal/is_trusted From: scott@paragonie.com (Scott Arciszewski) On Thu, Jun 24, 2021 at 2:10 AM Stephen Reay wro= te: > > > > On 24 Jun 2021, at 08:30, Scott Arciszewski wrote: > > On Wed, Jun 23, 2021, 9:23 PM Bruce Weirdan wrote: > > On Thu, Jun 24, 2021 at 3:41 AM Scott Arciszewski > wrote: > > The failure condition of this query is > "return all rows from the table already being queried", not "return > arbitrary data the attacker selects from any table that the > application can read". > > > Imagine that was a DELETE rather than SELECT and the failure condition > becomes 'the table is emptied'. > It may have less disastrous consequences (depending on how good your > backup / restore procedures are) compared to arbitrary reads you > demonstrated, but it is still, quite clearly, a glaring security hole > caused by user input in SQL query - AKA SQL injection in layman's > terms. > > it differs from Injection vulnerabilities in one > fundamental way: The attacker cannot change the structure of the SQL > query being executed. > > > I would say replacing a column name with value is changing the > structure of SQL query, and, basically, in exactly the way you > describe SQL injection: confusing the code (column name) with data. > > I wholeheartedly welcome this RFC as it was originally proposed: > is_literal() doing exactly what it says on the tin, without any > security claims. But it has gone far from there real quick and now > people can't even name the thing. > > > -- > Best regards, > Bruce Weirdan mailto: > weirdan@gmail.com > > > > > We can agree that it is a bug. We don't agree on the definition of SQL > injection. > > Changing a column name to a number (which prepared statements shouldn't > allow in the first place) is a bug. This changes the effect of the comman= d, > but the *structure* of the query remains unchanged. > > > Hi Scott, > > I wrote that example where an integer could be dangerous. > > So firstly - just to clarify, because some replies seemed to be confused = on the topic, as was literally mentioned in the original comment, it is def= initely not correct behaviour - it=E2=80=99s a developer mistake that might= work some of the time, and thus go unnoticed in testing. If you can show m= e a developer who=E2=80=99s never inadvertently passed the wrong parameter = in some condition, I=E2=80=99ll show you an imaginary developer. > > > Additionally - pointing out that this is a "developer error=E2=80=9D does= n=E2=80=99t help your case. Using non-parameterised queries should already = be a =E2=80=9Cdeveloper error=E2=80=9D for anyone who can walk and breathe = at the same time - and yet that usage is being actively encouraged if this = function supports integers. I=E2=80=99ve still seen zero responses about le= gitimate reasons this needs to support integers - giving people a shitty wa= y to build an IN() clause is not legitimate. Parameterisation exists, and w= orks. > > > I don=E2=80=99t even understand why you mentioned prepared statements (I = guess you meant using parameterised queries?) - the column name inherently = can=E2=80=99t be parameterised - hence having to use a string substitution = in the query. > > > That part was weird and confusing, but not as odd as your claim that alte= ring the query, so that it causes the where clause to become moot, is not a= n SQL Injection? REALLY? That=E2=80=99s your claim? > > > I did a little research, and it turns out Wikipedia (https://en.wikipedia= .org/wiki/SQL_injection#Technical_implementations), Cloudflare (https://www= .cloudflare.com/en-au/learning/security/threats/sql-injection/), and OWASP = (https://owasp.org/www-community/attacks/SQL_Injection#example-2) all have = examples with a `1=3D1` type query manipulation. Do you want to write and t= ell them that they=E2=80=99re all wrong, or should I ask them to call you? > > Also, while researching the specifics of what is considered an =E2=80=9CS= QL Injection=E2=80=9D I came across an article, that talks specifically abo= ut the dangers of allowing user input (i.e. the thing `is_trusted` is meant= to prevent) as a column or table identifier. It=E2=80=99s from this little= organisation, you may have heard of them: =E2=80=9CParagon Initiative=E2= =80=9D (https://paragonie.com/blog/2015/05/preventing-sql-injection-in-php-= applications-easy-and-definitive-guide). > > > > I would absolutely make use of a function that tells me if the string giv= en is in fact from something controlled by the developer. But once that sam= e string can also include input from the request or the environment or what= ever by nature of integers, the function becomes useless for the stated pur= pose. > > > Cheers > > Stephen > One other thing. What do you think the result of this code will be? How many rows do you think it will spit out? ``` setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Let's create a table: $db->exec("CREATE TABLE foo (username TEXT, pwhash TEXT, email TEXT)"); // Dummy rows: $db->exec("INSERT INTO foo(username, pwhash, email) VALUES ('scott', 'lolno', 'scott@paragonie.com')"); $db->exec("INSERT INTO foo(username, pwhash, email) VALUES ('robyn', 'fake!', 'robyn@paragonie.com')"); // Okay, now let's do a prepared statement, with an integer field $int =3D 12345; $stmt =3D $db->prepare("SELECT * FROM foo WHERE {$int} =3D ?"); $stmt->execute([12345]); var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); ```