I'm contemplating the construction of an interface for advanced or dedicated users of a database, with a text box in which they type their queries as XQuery modules. (Non-advanced and non-dedicated users will make do with a variety of pre-defined queries; this interface is intended to provide an open-ended query interface for the few users who will need it.)
One issue that arises is security: any use of an eval() function opens the door to code injection attacks. For now, I'm inclined to think that BaseX's rule that xquery:eval() does not accept updating expressions suffices to protect the database from harm. Parsing the query and declining potentially harmful queries is also a possibility, if I can persuade myself I can distinguish harmful from harmless queries. (Comments and advice on this topic welcome, even though it's not the focus of this question.)
A second issue is resource usage. Imagine a hostile user who writes a query designed to tie up the server for a long time and consume a lot of memory. Or imagine a non-hostile but naive user who unintentionally concocts an extremely expensive query.
Is there a way to put a call to eval() (or any call to the BaseX HTTP server) into some kind of box and specify limits on the amount of CPU time, the amount of clock time, and/or the amount of storage to be available for its evaluation?
Michael
I'm contemplating the construction of an interface for advanced or dedicated users of a database, with a text box in which they type their queries as XQuery modules. (Non-advanced and non-dedicated users will make do with a variety of pre-defined queries; this interface is intended to provide an open-ended query interface for the few users who will need it.)
If possible, xquery:eval() should be avoided for such operations (we may eventually rename it to evil()). The solution which you find on our homepage [1] is based on our REST interface, and a user whose permissions are restricted to reading the example databases. This way, queries like "file:list('.')" will be rejected. The query timeout (which doesn’t apply to admin queries [2]) has been set to 10 seconds. There is currently no way to restrict memory resources in this demo, because the query will run in the same virtual machine as the server instance. One solution could be to start a new BaseX (server) instance with limited memory (-Xmx).
Feedback from other users is welcome. Christian
[1] http://basex.org/products/live-demo/ [2] http://docs.basex.org/wiki/Options#TIMEOUT
On Jun 24, 2013, at 10:02 PM, Christian Grün wrote:
I'm contemplating the construction of an interface for advanced or dedicated users of a database, with a text box in which they type their queries as XQuery modules. (Non-advanced and non-dedicated users will make do with a variety of pre-defined queries; this interface is intended to provide an open-ended query interface for the few users who will need it.)
If possible, xquery:eval() should be avoided for such operations (we may eventually rename it to evil()).
That would certainly catch the developer's attention!
The solution which you find on our homepage [1] is based on our REST interface, and a user whose permissions are restricted to reading the example databases. This way, queries like "file:list('.')" will be rejected.
Can you provide more information on how this is implemented on the BaseX site? I can see how the user's query string can be wrapped in a rest:query element (and even how I can set the context for them), and submitted to the server in the normal way. Is that what is happening in the BaseX demo interface? (Is the source code available somewhere? I don't see an obvious place to look for it in the GitHub repository.)
I had been envisaging something like
import module namespace cqi := "http://example.org/corpus-query-interface"; declare variable $cqi:query as xs:string external;
if (cqi:nanny-says-ok($cqi:query)) then xquery:eval($query) else <error>That query goes too far!</error>
My idea was that the cqi:nanny-says-ok() function can do some simple vetting of the query to weed out constructs like calls to doc('file:///etc/passwd') but allow calls to doc() for documents on other Web sites. I was worried about the rest:query interface: I can make my PHP proxy do all the checking I would have done with cqi:nanny-says-ok(), but I can't prevent an adversary from sending an HTTP request directly to the BaseX server and bypassing the PHP proxy -- so I wanted to do my checking in XQuery.
It may be that having queries run under an appropriately restricted user provides all the security I need. A user with read-only access cannot modify the database (and also cannot use doc(), as I've just discovered), and that protects against the primary risk I am concerned with.
The query timeout (which doesn’t apply to admin queries [2]) has been set to 10 seconds. There is currently no way to restrict memory resources in this demo, because the query will run in the same virtual machine as the server instance. One solution could be to start a new BaseX (server) instance with limited memory (-Xmx).
This is very helpful; I should have remembered the TIMEOUT option, but didn't. It protects me against the second risk I'm worried about. On memory usage I will just take my chances for now.
Can you provide more information on how this is implemented on the BaseX site?
[…] I was worried about the rest:query interface: I can make my PHP proxy do all the checking I would have done with cqi:nanny-says-ok(), but I can't prevent an adversary from sending an HTTP request directly to the BaseX server and bypassing the PHP proxy -- so I wanted to do my checking in XQuery.
I’d like to, but I must admit that the existing implementation includes some irrelevant indirections, which is one of the reasons (beside time constraints) why we didn’t make it public so far. The general principle is simple, however: the query is sent to the plain REST service of a remote BaseX HTTP Server, and the chosen user has read-only permissions. If you want to avoid that the BaseX server is visible and can be controlled from outside, you can e.g. restrict the SERVERHOST option to localhost [1,2].
Maybe this already solves part of the challenge? Christian
[1] http://docs.basex.org/wiki/Options#SERVERHOST [2] http://docs.basex.org/wiki/Startup_Options#BaseX_Server
As someone coming at this from the other direction - I would like to allow dynamic execution of anything (given the appropriate passwords, of course). I am trying to implement something similar to eXide [1] to run on an Android phone. I am using
client:connect('localhost', 1984, 'admin', 'admin') ! client:query(.,$src) client:connect('localhost', 1984, 'admin', 'admin') !
to execute arbitrary strings from a RESTXQ application. This does allow updating expressions.
I was surprised, but not too concerned, that "file:list('.')" requires create permission. I could not find this, or similar information in the Wiki. It would be a useful addition to document it there.
I believe, at the Java level, these permissions are described using an annotation and I wonder if there would be value in surfacing these as a custom BaseX annotation in the module function stubs in etc\modules and even allowing their use with pure XQuery code?
Regards /Andy
[1] http://exist-db.org/exist/apps/eXide/docs/doc.html
On Fri, Jun 28, 2013 at 9:20 AM, Christian Grün christian.gruen@gmail.comwrote:
Can you provide more information on how this is implemented on the BaseX site?
[…] I was worried about the rest:query interface: I can make my PHP proxy do all the checking I would have done with cqi:nanny-says-ok(), but I can't prevent an adversary from sending an HTTP request directly to the BaseX server and bypassing the PHP proxy -- so I wanted to do my checking in XQuery.
I’d like to, but I must admit that the existing implementation includes some irrelevant indirections, which is one of the reasons (beside time constraints) why we didn’t make it public so far. The general principle is simple, however: the query is sent to the plain REST service of a remote BaseX HTTP Server, and the chosen user has read-only permissions. If you want to avoid that the BaseX server is visible and can be controlled from outside, you can e.g. restrict the SERVERHOST option to localhost [1,2].
Maybe this already solves part of the challenge? Christian
[1] http://docs.basex.org/wiki/Options#SERVERHOST [2] http://docs.basex.org/wiki/Startup_Options#BaseX_Server _______________________________________________ BaseX-Talk mailing list BaseX-Talk@mailman.uni-konstanz.de https://mailman.uni-konstanz.de/mailman/listinfo/basex-talk
That code got a little garbled. I meant: client:connect('localhost', 1984, $user, $password) ! client:query(.,$src)
See http://docs.basex.org/wiki/Client_Module#client:query
On Fri, Jun 28, 2013 at 11:52 AM, Andy Bunce bunce.andy@gmail.com wrote:
As someone coming at this from the other direction - I would like to allow dynamic execution of anything (given the appropriate passwords, of course). I am trying to implement something similar to eXide [1] to run on an Android phone. I am using
client:connect('localhost', 1984, 'admin', 'admin') ! client:query(.,$src) client:connect('localhost', 1984, 'admin', 'admin') !
to execute arbitrary strings from a RESTXQ application. This does allow updating expressions.
I was surprised, but not too concerned, that "file:list('.')" requires create permission. I could not find this, or similar information in the Wiki. It would be a useful addition to document it there.
I believe, at the Java level, these permissions are described using an annotation and I wonder if there would be value in surfacing these as a custom BaseX annotation in the module function stubs in etc\modules and even allowing their use with pure XQuery code?
Regards /Andy
[1] http://exist-db.org/exist/apps/eXide/docs/doc.html
On Fri, Jun 28, 2013 at 9:20 AM, Christian Grün <christian.gruen@gmail.com
wrote:
Can you provide more information on how this is implemented on the BaseX site?
[…] I was worried about the rest:query interface: I can make my PHP proxy do all the checking I would have done with cqi:nanny-says-ok(), but I can't prevent an adversary from sending an HTTP request directly to the BaseX server and bypassing the PHP proxy -- so I wanted to do my checking in XQuery.
I’d like to, but I must admit that the existing implementation includes some irrelevant indirections, which is one of the reasons (beside time constraints) why we didn’t make it public so far. The general principle is simple, however: the query is sent to the plain REST service of a remote BaseX HTTP Server, and the chosen user has read-only permissions. If you want to avoid that the BaseX server is visible and can be controlled from outside, you can e.g. restrict the SERVERHOST option to localhost [1,2].
Maybe this already solves part of the challenge? Christian
[1] http://docs.basex.org/wiki/Options#SERVERHOST [2] http://docs.basex.org/wiki/Startup_Options#BaseX_Server _______________________________________________ BaseX-Talk mailing list BaseX-Talk@mailman.uni-konstanz.de https://mailman.uni-konstanz.de/mailman/listinfo/basex-talk
As someone coming at this from the other direction - I would like to allow dynamic execution of anything (given the appropriate passwords, of course). I am trying to implement something similar to eXide [1] to run on an Android phone.
…exciting!
I was surprised, but not too concerned, that "file:list('.')" requires create permission. I could not find this, or similar information in the Wiki. It would be a useful addition to document it there.
True; there are various functions that require create or admin permissions, and that should be properly documented in our function modules. In the given case, the restriction exists to prevent ordinary users from traversing through the file system of an external server unless they are allowed to create new databases from local resources. The restriction to four types of permissions (read, write, create, admin) is prone to such confusions, but we observed that things soon get worse if we try to extend this pattern by additional permissions without having more thoughts on what we try to attain. Various concepts have been discussed in the past, such as introducing database owners, roles, or security sandboxes.
I believe, at the Java level, these permissions are described using an annotation and I wonder if there would be value in surfacing these as a custom BaseX annotation in the module function stubs in etc\modules and even allowing their use with pure XQuery code?
That’s an interesting thought. Our team member Marcel is currently working on security annotations [1]; we may need to see if we can combine or extend them in a reasonable manner… Bit this could turn out to be a little bit tricky, because the proposed annotations don’t interact with the BaseX standard users/permissions. Instead, they were built to be allow users to log in on an application level (usually with RESTXQ).
Best, Christian
On Fri, 2013-06-28 at 10:20 +0200, Christian Grün wrote:
[...] If you want to avoid that the BaseX server is visible and can be controlled from outside, you can e.g. restrict the SERVERHOST option to localhost [1,2].
I do that on fromoldbooks.org, but when I upgraded basex it of course started using a different conf file (without me realising) and became insecure. It would be better if listening on localhost could be the default: make packages reasonably secure by default.
Liam
basex-talk@mailman.uni-konstanz.de