Python add new queries for clear-text logging and storage.

This commit is contained in:
Mark Shannon
2019-06-26 11:40:33 +01:00
parent 79ebd5652a
commit 15bb8b5f70
6 changed files with 172 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CleartextStorage.qhelp" /></qhelp>

View File

@@ -0,0 +1,41 @@
/**
* @name Clear-text logging of sensitive information
* @description Logging sensitive information without encryption or hashing can
* expose it to an attacker.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/clear-text-storage-of-sensitive-data
* @tags security
* external/cwe/cwe-312
* external/cwe/cwe-315
* external/cwe/cwe-359
*/
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText
class CleartextLoggingConfiguration extends TaintTracking::Configuration {
CleartextLoggingConfiguration() { this = "ClearTextLogging" }
override predicate isSource(TaintSource src) {
src instanceof SensitiveData::Source
}
override predicate isSink(TaintSink sink) {
sink instanceof ClearTextLogging::Sink
}
}
from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink
where config.hasFlowPath(source, sink)
select sink.getSink(), source, sink, "Sensitive data returned by $@ is stored here.",
source.getSource(), source.getNode().(SensitiveData::Source).repr()

View File

@@ -0,0 +1,52 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive information that is stored unencrypted is accessible to an attacker
who gains access to the storage. This is particularly important for cookies,
which are stored on the machine of the end-user.
</p>
</overview>
<recommendation>
<p>
Ensure that sensitive information is always encrypted before being stored.
If possible, avoid placing sensitive information in cookies altogether.
Instead, prefer storing, in the cookie, a key that can be used to look up the
sensitive information.
</p>
<p>
In general, decrypt sensitive information only at the point where it is
necessary for it to be used in cleartext.
</p>
<p>
Be aware that external processes often store the <code>standard
out</code> and <code>standard error</code> streams of the application,
causing logged sensitive information to be stored as well.
</p>
</recommendation>
<example>
<p>
The following example code stores user credentials (in this case, their password) in a cookie in plain text:
</p>
<sample src="examples/password_in_cookie.py"/>
<p>
Instead, the credentials should be encrypted, for instance by using the <code>cryptography</code> module, or not stored at all.
</example>
<references>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,40 @@
/**
* @name Clear-text storage of sensitive information
* @description Sensitive information stored without encryption or hashing can expose it to an
* attacker.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/clear-text-logging
* @tags security
* external/cwe/cwe-312
* external/cwe/cwe-315
* external/cwe/cwe-359
*/
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText
class CleartextStorageConfiguration extends TaintTracking::Configuration {
CleartextStorageConfiguration() { this = "ClearTextStorage" }
override predicate isSource(TaintSource src) {
src instanceof SensitiveData::Source
}
override predicate isSink(TaintSink sink) {
sink instanceof ClearTextStorage::Sink
}
}
from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink
where config.hasFlowPath(source, sink)
select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.",
source.getSource(), source.getNode().(SensitiveData::Source).repr()

View File

@@ -0,0 +1,10 @@
from flask import Flask, make_response, request
app = Flask("Leak password")
@app.route('/')
def index():
password = request.args.get("password")
resp = make_response(render_template(...))
resp.set_cookie("password", password)
return resp

View File

@@ -11,7 +11,7 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.web.HttpRequest
/**
* Provides heuristics for identifying names related to sensitive information.
@@ -142,7 +142,7 @@ module SensitiveData {
}
override string repr() {
result = "Call returning " + data.repr()
result = "a call returning " + data.repr()
}
}
@@ -166,6 +166,28 @@ module SensitiveData {
}
private class SensitiveRequestParameter extends SensitiveData::Source {
SensitiveData data;
SensitiveRequestParameter() {
this.(CallNode).getFunction().(AttrNode).getName() = "get" and
exists(string sensitive |
this.(CallNode).getAnArg().refersTo(any(StringObject s | s.getText() = sensitive)) and
data = HeuristicNames::getSensitiveDataForName(sensitive)
)
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
override string repr() {
result = "a request parameter containing " + data.repr()
}
}
}
//Backwards compatibility