Skip to main content

Security

In this section we'll learn all about security and helping to understand how to prevent common DQL injection exploit.

DQL Injection#

If you come from a SQL world you might be familiar with the term SQL Injection.

DQL Injection is a similar beast but (overall less dangerous). Let's first understand what is an Injection.

Every time a query is dynamically generated (with dqlx or not) strings gets concatenated together to create the final query.

The problem arise when your queries are generated based on User Inputs. If you are accepting user inputs that gets directly mapped into a query, the "Exploiter" might figure out a way to send an input which might alter the original query that you originally meant to run.

This mean that the "Exploiter" might be able to request more data than he supposed to, or even worst it could delete or alter important information.

dqlx At Rescue#

dqlx takes security as one of the reasons for this library to also exists.
By default dqlx is DQL Injection safe as of v0.2

All the inputs provided to the underline functions exposed by dqlx are all escaped by default. Making it impossible to concatenate pieces of strings that might alter the query.

How does exactly dqlx achieve this? In 2 ways

  • Using GraphQL Query Variables
  • Escaping all predicates with <> while also removing dangerous (not allowed) characters ^ } | { \\ , < > "

GraphQL Variables#

dqlx will replace all the values you provided with a GraphQL Variable.
For instance when you write a query that looks like this:

query, variables, err := db.Query(dqlx.HasFn("name")).
Select(`
uid
name
animal
`).
Filter(
dql.Or{
dql.And{
dqlx.Eq{"name": "Ollie", "animal": "Cat"},
dqlx.Gt{"age": 2},
},
dql.And{
dqlx.Eq{"name": "Leo", "animal": "Cat"},
dqlx.Gt{"age": 3},
},
},
)
.ToDQL()

All the values that are assigned to those filters will be replaced by a variable, thus making it DQL Injection free

query RootQuery($0: string,$1: string,$3: int, $4: string, $5: string, $6: int) {
rootQuery(func: has(name)) @filter( (eq(name,$0) AND eq(animal,$1) AND gt(age, $3)) OR (eq(name,$4) AND eq(animal,$5) AND gt(age, $6))) {
uid
name
animal
}
}

Escaping Predicates#

From version v0.2 dqlx achieve maximum security by parsing and escaping all the predicates within the query.

In dql when you surround a predicate with <>, Dgraph will escape any special character as well as treat that expression exclusively as a predicate and will not execute any keyword within it.

For instance if we write this dqlx query

query, variables, err := db.Query(dqlx.HasFn("name")).
Select(`
uid
name
animal
breed
`).
Filter(
dqlx.Eq("animal", "Cat"),
dqlx.Eq("animal", "Dog"),
)
.ToDQL()

you'll get:

query RootQuery($0:string, $1:int, $2:int) {
<rootQuery>(func: eq(<name>,$0)) @filter(eq(<animal>,$1) AND eq(<animal>, $2)) {
<uid>
<name>
<animal>
<breed>
}
}

even if you intentionally want to expose yourself to an attacker doing something along this line (and not validating the input):

filterHas := req.Params("filterHas")
db.Query(dqlx.HasFn(filterHas)).
Select("uid")

The attacker might only able to play around that 1 field that you gave access to, but he will not be able to exploit the query in any way, for example sending some dodgy input to try to customise the query

Be Careful!#

Yes, dqlx is very safe by default, but of course you can make it unsafe if you don't stick to the rules.

In short, in the whole library there is 1 single function Expr that allows you to write raw parts of the query and it will get concatenated as is.

If you expose that function to a user input you are subjected to DQL injection. Handle it with care.

Secondly I always recommend to never trust inputs and always validate what you get from the internet.

Reporting Vulnerabilities#

In case of a security vulnerability contact me directly via email at: fabri.feno@gmail.com

All security vulnerabilities will be promptly addressed