Interpolated strings and SQL
Timon Gehr
timon.gehr at gmx.ch
Fri Jan 12 22:54:20 UTC 2024
On 1/12/24 07:13, Walter Bright wrote:
> I'd like to see an example of how DIP1027 does not prevent an injection
> attack.
```d
// mock SQL
import std.format, std.variant;
class Sqlite{
this(string){}
Sqlite query(string command,scope Variant[int] args=null){
writeln("EXECUTING");
writeln(command);
if(args.length){
writeln("ARGS:");
foreach(k,v;args){
if(v!=Variant.init)
writefln(i"?$k = ($v)");
}
}
writeln("DONE");
return this;
}
struct Row{ int opIndex(int i){ return 0; } }
int opApply(scope int delegate(Row) dg){
writeln("ITERATING OVER ROWS");
return 0;
}
}
struct Statement{
Sqlite db;
string query;
Variant[int] args;
void bind(T)(int i,T arg){
args[i]=Variant(arg);
}
void execute(){
db.query(query,args);
}
}
auto execi(Args...)(Sqlite db, Args args) {
// sqlite lets you do ?1, ?2, etc
string query = () { // note: parsing done at runtime
string sql;
int number;
import std.conv;
auto fmt = args[0];
for (size_t i = 0; i < fmt.length; ++i)
{
char c = fmt[i];
if (c == '%' && i + 1 < fmt.length && fmt[i + 1] == 's')
{
sql ~= "?" ~ to!string(++number);
++i;
}
else if (c == '%' && i + 1 < fmt.length && fmt[i + 1] == '%')
++i; // skip escaped %
else
sql ~= c;
}
return sql;
}();
auto statement = Statement(db, query);
int number;
foreach(arg; args[1 .. args.length]) {
statement.bind(++number, arg);
}
return statement.execute();
}
import std.stdio;
void main() {
auto db = new Sqlite(":memory:");
db.query("CREATE TABLE Students (id INTEGER, name TEXT)");
// you might think this is sql injection... and you'd be right! the lib
// cannot use rich metadata because it is not provided by the istring
// therefore, it cannot verify that the user didn't construct the
// query themselves in an unsafe way
int id = 1;
string name = "Robert'); DROP TABLE Students;--";
db.execi(i"INSERT INTO sample VALUES ($(id), '$(name)')".format);
foreach(row; db.query("SELECT * from sample"))
writeln(row[0], ": ", row[1]);
}
```
Prints:
EXECUTING
CREATE TABLE Students (id INTEGER, name TEXT)
DONE
EXECUTING
INSERT INTO sample VALUES (1, 'Robert'); DROP TABLE Students;--')
DONE
EXECUTING
SELECT * from sample
DONE
ITERATING OVER ROWS
https://xkcd.com/327/
More information about the Digitalmars-d
mailing list