Merge pull request #13561 from amammad/amammad-python-WebAppsConstatntSecretKeys

Python: Flask & Django Constant Secret Key initialization
This commit is contained in:
Rasmus Wriedt Larsen
2023-08-21 11:39:19 +02:00
committed by GitHub
23 changed files with 696 additions and 0 deletions

View File

@@ -4372,6 +4372,30 @@ private module StdlibPrivate {
preservesValue = false
}
}
/**
* A flow summary for `os.getenv` / `os.getenvb`
*
* See https://devdocs.io/python~3.11/library/os#os.getenv
*/
class OsGetEnv extends SummarizedCallable {
OsGetEnv() { this = "os.getenv" }
override DataFlow::CallCfgNode getACall() {
result = API::moduleImport("os").getMember(["getenv", "getenvb"]).getACall()
}
override DataFlow::ArgumentNode getACallback() {
result =
API::moduleImport("os").getMember(["getenv", "getenvb"]).getAValueReachableFromSource()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input in ["Argument[1]", "Argument[default:]"] and
output = "ReturnValue" and
preservesValue = true
}
}
}
// ---------------------------------------------------------------------------

View File

@@ -0,0 +1,57 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
Flask and Django require a Securely signed key for singing the session cookies. most of the time developers rely on load hardcoded secret keys from a config file or python code. this proves that the way of hardcoded secret can make problems when you forgot to change the constant secret keys.
</p>
</overview>
<recommendation>
<p>
In Flask Consider using a secure random generator with <a href="https://docs.python.org/3/library/secrets.html#secrets.token_hex">Python standard secrets library</a>
</p>
<p>
In Django Consider using a secure random generator with "get_random_secret_key()"" method from "django.core.management.utils".
</p>
</recommendation>
<example>
<p>Safe Django SECRET_KEY</p>
<sample src="examples/example_Django_safe.py" />
<p>Unsafe Django SECRET_KEY Example:</p>
<sample src="examples/example_Django_unsafe.py" />
<p>Safe Flask SECRET_KEY Example:</p>
<sample src="examples/example_Flask_safe.py" />
<sample src="examples/example_Flask_unsafe.py" />
<p>Unsafe Flask SECRET_KEY Example:</p>
<sample src="examples/example_Flask_unsafe2.py" />
<p>config1.py</p>
<sample src="examples/config1.py" />
<p>config2.py</p>
<sample src="examples/config2.py" />
<p>config3.py</p>
<sample src="examples/config3.py" />
<p>__init__.py</p>
<sample src="examples/settings/__init__.py" />
</example>
<references>
<li>
<a href="https://flask.palletsprojects.com/en/2.3.x/config/#SECRET_KEY">Flask Documentation</a>
</li>
<li>
<a href="https://docs.djangoproject.com/en/4.2/ref/settings/#secret-key">Django Documentation</a>
</li>
<li>
<a href="https://flask-jwt-extended.readthedocs.io/en/stable/basic_usage.html#basic-usage">Flask-JWT-Extended Documentation</a>
</li>
<li>
<a href="https://www.horizon3.ai/cve-2023-27524-insecure-default-configuration-in-apache-superset-leads-to-remote-code-execution/">CVE-2023-27524 - Apache Superset had multiple CVEs related to this kind of Vulnerability</a>
</li>
<li>
<a href="https://nvd.nist.gov/vuln/detail/CVE-2020-17526">CVE-2020-17526 - Apache Airflow had multiple CVEs related to this kind of Vulnerability</a>
</li>
<li>
<a href="https://nvd.nist.gov/vuln/detail/CVE-2021-41192">CVE-2021-41192 - Redash was assigning a environment variable with a default value which it was assigning the default secrect if the environment variable does not exists</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,64 @@
/**
* @name Initializing SECRET_KEY of Flask application with Constant value
* @description Initializing SECRET_KEY of Flask application with Constant value
* files can lead to Authentication bypass
* @kind path-problem
* @id py/flask-constant-secret-key
* @problem.severity error
* @security-severity 8.5
* @precision high
* @tags security
* experimental
* external/cwe/cwe-287
*/
import python
import experimental.semmle.python.Concepts
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.TaintTracking
import WebAppConstantSecretKeyDjango
import WebAppConstantSecretKeyFlask
import semmle.python.filters.Tests
newtype TFrameWork =
Flask() or
Django()
module WebAppConstantSecretKeyConfig implements DataFlow::StateConfigSig {
class FlowState = TFrameWork;
predicate isSource(DataFlow::Node source, FlowState state) {
state = Flask() and FlaskConstantSecretKeyConfig::isSource(source)
or
state = Django() and DjangoConstantSecretKeyConfig::isSource(source)
}
predicate isBarrier(DataFlow::Node node) {
node.getLocation().getFile().inStdlib()
or
// To reduce FP rate, the following was added
node.getLocation()
.getFile()
.getRelativePath()
.matches(["%test%", "%demo%", "%example%", "%sample%"]) and
// but that also meant all data-flow nodes in query tests were excluded... so we had
// to add this:
not node.getLocation().getFile().getRelativePath().matches("%query-tests/Security/CWE-287%")
}
predicate isSink(DataFlow::Node sink, FlowState state) {
state = Flask() and FlaskConstantSecretKeyConfig::isSink(sink)
or
state = Django() and DjangoConstantSecretKeyConfig::isSink(sink)
}
}
module WebAppConstantSecretKey = TaintTracking::GlobalWithState<WebAppConstantSecretKeyConfig>;
import WebAppConstantSecretKey::PathGraph
from WebAppConstantSecretKey::PathNode source, WebAppConstantSecretKey::PathNode sink
where WebAppConstantSecretKey::flowPath(source, sink)
select sink, source, sink, "The SECRET_KEY config variable is assigned by $@.", source,
" this constant String"

View File

@@ -0,0 +1,52 @@
import python
import experimental.semmle.python.Concepts
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.TaintTracking
import WebAppConstantSecretKeySource
module DjangoConstantSecretKeyConfig {
/**
* Sources are Constants that without any Tainting reach the Sinks.
* Also Sources can be the default value of getenv or similar methods
* in a case that no value is assigned to Desired SECRET_KEY environment variable
*/
predicate isSource(DataFlow::Node source) { source instanceof WebAppConstantSecretKeySource }
/**
* Holds if There is a sink like following SECRET_KEY Assignments
* ```python
*from django.conf import settings
*settings.configure(
* SECRET_KEY="constant",
*)
* # or
*settings.SECRET_KEY = "constant"
* ```
*/
predicate isSink(DataFlow::Node sink) {
exists(API::moduleImport("django")) and
(
exists(AssignStmt e | e.getTarget(0).(Name).getId() = ["SECRET_KEY", "SECRET_KEY_FALLBACKS"] |
sink.asExpr() = e.getValue()
)
or
exists(API::Node settings |
settings =
API::moduleImport("django").getMember("conf").getMember(["global_settings", "settings"]) and
sink =
settings
.getMember("configure")
.getKeywordParameter(["SECRET_KEY_FALLBACKS", "SECRET_KEY"])
.asSink()
)
or
exists(DataFlow::AttrWrite attr |
attr.getAttributeName() = ["SECRET_KEY_FALLBACKS", "SECRET_KEY"] and
sink = attr.getValue()
)
) and
exists(sink.getScope().getLocation().getFile().getRelativePath()) and
not sink.getScope().getLocation().getFile().inStdlib()
}
}

View File

@@ -0,0 +1,139 @@
import python
import experimental.semmle.python.Concepts
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.TaintTracking
import WebAppConstantSecretKeySource
/**
* with using flask-session package, there is no jwt exists in cookies in user side
* ```python
*import os
*from flask import Flask, session
*app = Flask(__name__)
* ```
*/
module FlaskConstantSecretKeyConfig {
/**
* `flask.Flask()`
*/
API::Node flaskInstance() {
result = API::moduleImport("flask").getMember("Flask").getASubclass*()
}
/**
* Sources are Constants that without any Tainting reach the Sinks.
* Also Sources can be the default value of getenv or similar methods
* in a case that no value is assigned to Desired SECRET_KEY environment variable
*/
predicate isSource(DataFlow::Node source) { source instanceof WebAppConstantSecretKeySource }
/**
* Sinks are one of the following kinds, some of them are directly connected to a flask Instance like
* ```python
* app.config['SECRET_KEY'] = 'CHANGEME1'
* app.secret_key = 'CHANGEME2'
* app.config.update(SECRET_KEY="CHANGEME3")
* app.config.from_mapping(SECRET_KEY="CHANGEME4")
* ```
* other Sinks are SECRET_KEY Constants Variables that are defined in separate files or a class in those files like:
* ```python
* app.config.from_pyfile("config.py")
* app.config.from_object('config.Config')
*```
* we find these files with `FromObjectFileName` DataFlow Configuration
* note that "JWT_SECRET_KEY" is same as "SECRET_KEY" but it is belong to popular flask-jwt-extended library
*/
predicate isSink(DataFlow::Node sink) {
(
exists(API::Node n | n = flaskInstance().getReturn() |
sink =
[
n.getMember("config").getSubscript(["SECRET_KEY", "JWT_SECRET_KEY"]).asSink(),
n.getMember("config")
.getMember(["update", "from_mapping"])
.getACall()
.getArgByName(["SECRET_KEY", "JWT_SECRET_KEY"])
]
)
or
exists(DataFlow::AttrWrite attr |
attr.getObject().getALocalSource() = flaskInstance().getACall() and
attr.getAttributeName() = ["secret_key", "jwt_secret_key"] and
sink = attr.getValue()
)
or
exists(SecretKeyAssignStmt e | sink.asExpr() = e.getValue())
) and
exists(sink.getScope().getLocation().getFile().getRelativePath()) and
not sink.getScope().getLocation().getFile().inStdlib()
}
/**
* An Assignments like `SECRET_KEY = ConstantValue`
* and `SECRET_KEY` file must be the Location that is specified in argument of `from_object` or `from_pyfile` methods
*/
class SecretKeyAssignStmt extends AssignStmt {
SecretKeyAssignStmt() {
exists(string configFileName, string fileNamehelper, DataFlow::Node n1, File file |
fileNamehelper = [flaskConfiFileName(n1), flaskConfiFileName2(n1)] and
// because of `from_object` we want first part of `Config.AClassName` which `Config` is a python file name
configFileName = fileNamehelper.splitAt(".") and
file = this.getLocation().getFile()
|
(
if fileNamehelper = "__name__"
then
file.getShortName() = flaskInstance().asSource().getLocation().getFile().getShortName()
else (
fileNamehelper.matches("%.py") and
file.getShortName().matches("%" + configFileName + "%") and
// after spliting, don't look at %py% pattern
configFileName != ".py"
or
// in case of referencing to a directory which then we must look for __init__.py file
not fileNamehelper.matches("%.py") and
file.getRelativePath()
.matches("%" + fileNamehelper.replaceAll(".", "/") + "/__init__.py")
)
) and
this.getTarget(0).(Name).getId() = ["SECRET_KEY", "JWT_SECRET_KEY"]
) and
exists(this.getScope().getLocation().getFile().getRelativePath()) and
not this.getScope().getLocation().getFile().inStdlib()
}
}
/**
* Holds if there is a helper predicate that specify where the Flask `SECRET_KEY` variable location is defined.
* In Flask we have config files that specify the location of `SECRET_KEY` variable initialization
* and the name of these files are determined by
* `app.config.from_pyfile("configFileName.py")`
* or
* `app.config.from_object("configFileName.ClassName")`
*/
string flaskConfiFileName(API::CallNode cn) {
cn =
flaskInstance()
.getReturn()
.getMember("config")
.getMember(["from_object", "from_pyfile"])
.getACall() and
result =
[
cn.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText(),
cn.getParameter(0).asSink().asExpr().(Name).getId()
]
}
string flaskConfiFileName2(API::CallNode cn) {
cn =
API::moduleImport("flask")
.getMember("Flask")
.getASubclass*()
.getASuccessor*()
.getMember("from_object")
.getACall() and
result = cn.getParameter(0).asSink().asExpr().(StrConst).getText()
}
}

View File

@@ -0,0 +1,38 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
/** A bad source for secret key in web app configurations. */
class WebAppConstantSecretKeySource extends DataFlow::Node {
WebAppConstantSecretKeySource() {
(
// we should check whether there is a default value or not
exists(API::Node env |
env = API::moduleImport("environ").getMember("Env") and
// has default value
exists(API::Node param | param = env.getKeywordParameter("SECRET_KEY") |
param.asSink().asExpr().getASubExpression*() instanceof StrConst
) and
this = env.getReturn().getReturn().asSource()
)
or
this.asExpr() instanceof StrConst
or
exists(API::CallNode cn |
cn =
[
API::moduleImport("os").getMember("getenv").getACall(),
API::moduleImport("os").getMember("environ").getMember("get").getACall()
] and
cn.getNumArgument() = 2 and
DataFlow::localFlow(any(DataFlow::Node n | n.asExpr() instanceof StrConst), cn.getArg(1)) and
this.asExpr() = cn.asExpr()
)
) and
// followings will sanitize the get_random_secret_key of django.core.management.utils and similar random generators which we have their source code and some of them can be tracking by taint tracking because they are initilized by a constant!
exists(this.getScope().getLocation().getFile().getRelativePath()) and
not this.getScope().getLocation().getFile().inStdlib() and
// special sanitize case for get_random_secret_key and django-environ
not this.getScope().getLocation().getFile().getBaseName() = ["environ.py", "crypto.py"]
}
}

View File

@@ -0,0 +1,14 @@
"""Flask App configuration."""
import os
import random
FLASK_DEBUG = True
aConstant = 'CHANGEME2'
class Config:
SECRET_KEY = aConstant
SECRET_KEY = os.environ.get('envKey', random.randint)
SECRET_KEY = os.getenv('envKey', random.randint)
SECRET_KEY = os.getenv('envKey', aConstant)
SECRET_KEY = os.environ.get('envKey', aConstant)

View File

@@ -0,0 +1,23 @@
"""Flask App configuration."""
from os import environ
import os
import random
import configparser
FLASK_DEBUG = True
aConstant = 'CHANGEME2'
config = configparser.ConfigParser()
class Config:
SECRET_KEY = config["a"]["Secret"]
SECRET_KEY = config.get("key", "value")
SECRET_KEY = environ.get("envKey")
SECRET_KEY = aConstant
SECRET_KEY = os.getenv('envKey')
SECRET_KEY = os.environ.get('envKey')
SECRET_KEY = os.environ.get('envKey', random.randint)
SECRET_KEY = os.getenv('envKey', random.randint)
SECRET_KEY = os.getenv('envKey', aConstant)
SECRET_KEY = os.environ.get('envKey', aConstant)
SECRET_KEY = os.environ['envKey']

View File

@@ -0,0 +1,9 @@
"""Flask App configuration."""
aConstant = 'CHANGEME2'
SECRET_KEY = aConstant
class Config:
SECRET_KEY = aConstant

View File

@@ -0,0 +1,12 @@
"""Flask App configuration."""
import os
# General Config
FLASK_DEBUG = True
# if we are loading SECRET_KEY from config files then
# it is good to check default value always, maybe
# the user responsible for setup the application make a mistake
# and has not changed the default SECRET_KEY value
SECRET_KEY = os.getenv('envKey', "A_CONSTANT_SECRET") # A_CONSTANT_SECRET
if SECRET_KEY == "A_CONSTANT_SECRET":
raise "not possible"

View File

@@ -0,0 +1,32 @@
import sys
from django.conf import settings
from django.conf import global_settings
from django.urls import path
from django.http import HttpResponse
from django.core.management.utils import get_random_secret_key
settings.configure(
DEBUG=True,
SECRET_KEY=get_random_secret_key(),
ROOT_URLCONF=__name__,
)
global_settings.SECRET_KEY = get_random_secret_key()
settings.SECRET_KEY = get_random_secret_key()
def home(request):
return HttpResponse(settings.SECRET_KEY)
urlpatterns = [
path("", home),
]
if __name__ == "__main__":
from django.core.management import execute_from_command_line
if len(sys.argv) == 1:
sys.argv.append("runserver")
sys.argv.append("8080")
execute_from_command_line(sys.argv)

View File

@@ -0,0 +1,38 @@
import sys
import environ
from django.conf import settings
from django.conf import global_settings
from django.urls import path
from django.http import HttpResponse
env = environ.Env(
SECRET_KEY=(str, "AConstantKey")
)
env.read_env(env_file='.env')
# following is not safe if there is default value in Env(..)
settings.SECRET_KEY = env('SECRET_KEY')
settings.configure(
DEBUG=True,
SECRET_KEY="constant1",
ROOT_URLCONF=__name__,
)
global_settings.SECRET_KEY = "constant2"
settings.SECRET_KEY = "constant3"
def home(request):
return HttpResponse(settings.SECRET_KEY)
urlpatterns = [
path("", home),
]
if __name__ == "__main__":
from django.core.management import execute_from_command_line
if len(sys.argv) == 1:
sys.argv.append("runserver")
sys.argv.append("8080")
execute_from_command_line(sys.argv)

View File

@@ -0,0 +1,16 @@
from flask import Flask, session
from secrets import token_hex
app = Flask(__name__)
app.secret_key = token_hex(16)
app.config.from_pyfile("config3.py")
@app.route('/')
def CheckForSecretKeyValue():
# debugging whether secret_key is secure or not
return app.secret_key, session.get('logged_in')
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,23 @@
from flask import Flask, session
app = Flask(__name__)
aConstant = 'CHANGEME1'
app.config['SECRET_KEY'] = aConstant
app.secret_key = aConstant
app.config.update(SECRET_KEY=aConstant)
app.config.from_mapping(SECRET_KEY=aConstant)
app.config.from_pyfile("config.py")
app.config.from_pyfile("config2.py")
app.config.from_object('config.Config')
app.config.from_object('config2.Config')
app.config.from_object('settings')
@app.route('/')
def CheckForSecretKeyValue():
# debugging whether secret_key is secure or not
return app.secret_key, session.get('logged_in')
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,18 @@
from flask import Flask, session
app = Flask(__name__)
aConstant = 'CHANGEME1'
SECRET_KEY = aConstant
app.config.from_object(__name__)
@app.route('/')
def DEB_EX():
if 'logged_in' not in session:
session['logged_in'] = 'value'
# debugging whether secret_key is secure or not
return app.secret_key, session.__str__()
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,3 @@
import os
SECRET_KEY = "REDASH_COOKIE_SECRET"

View File

@@ -0,0 +1,42 @@
edges
| app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:5:28:5:36 | ControlFlowNode for aConstant |
| app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:6:18:6:26 | ControlFlowNode for aConstant |
| app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:7:30:7:38 | ControlFlowNode for aConstant |
| app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:8:36:8:44 | ControlFlowNode for aConstant |
| config.py:7:1:7:9 | GSSA Variable aConstant | config.py:12:18:12:26 | ControlFlowNode for aConstant |
| config.py:7:1:7:9 | GSSA Variable aConstant | config.py:12:18:12:26 | ControlFlowNode for aConstant |
| config.py:7:1:7:9 | GSSA Variable aConstant | config.py:17:38:17:46 | ControlFlowNode for aConstant |
| config.py:7:1:7:9 | GSSA Variable aConstant | config.py:18:43:18:51 | ControlFlowNode for aConstant |
| config.py:7:13:7:23 | ControlFlowNode for Str | config.py:7:1:7:9 | GSSA Variable aConstant |
| config.py:12:18:12:26 | ControlFlowNode for aConstant | config.py:17:38:17:46 | ControlFlowNode for aConstant |
| config.py:12:18:12:26 | ControlFlowNode for aConstant | config.py:18:43:18:51 | ControlFlowNode for aConstant |
| config.py:17:38:17:46 | ControlFlowNode for aConstant | config.py:17:18:17:47 | ControlFlowNode for Attribute() |
| config.py:17:38:17:46 | ControlFlowNode for aConstant | config.py:18:43:18:51 | ControlFlowNode for aConstant |
| config.py:18:43:18:51 | ControlFlowNode for aConstant | config.py:18:18:18:52 | ControlFlowNode for Attribute() |
nodes
| app_safe.py:5:28:5:37 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| app_unsafe.py:5:28:5:36 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| app_unsafe.py:6:18:6:26 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| app_unsafe.py:7:30:7:38 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| app_unsafe.py:8:36:8:44 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| config2.py:5:14:5:24 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| config.py:7:1:7:9 | GSSA Variable aConstant | semmle.label | GSSA Variable aConstant |
| config.py:7:13:7:23 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| config.py:12:18:12:26 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| config.py:12:18:12:26 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| config.py:17:18:17:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| config.py:17:38:17:46 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
| config.py:18:18:18:52 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| config.py:18:43:18:51 | ControlFlowNode for aConstant | semmle.label | ControlFlowNode for aConstant |
subpaths
#select
| app_safe.py:5:28:5:37 | ControlFlowNode for Str | app_safe.py:5:28:5:37 | ControlFlowNode for Str | app_safe.py:5:28:5:37 | ControlFlowNode for Str | The SECRET_KEY config variable is assigned by $@. | app_safe.py:5:28:5:37 | ControlFlowNode for Str | this constant String |
| app_unsafe.py:5:28:5:36 | ControlFlowNode for aConstant | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:5:28:5:36 | ControlFlowNode for aConstant | The SECRET_KEY config variable is assigned by $@. | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | this constant String |
| app_unsafe.py:6:18:6:26 | ControlFlowNode for aConstant | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:6:18:6:26 | ControlFlowNode for aConstant | The SECRET_KEY config variable is assigned by $@. | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | this constant String |
| app_unsafe.py:7:30:7:38 | ControlFlowNode for aConstant | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:7:30:7:38 | ControlFlowNode for aConstant | The SECRET_KEY config variable is assigned by $@. | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | this constant String |
| app_unsafe.py:8:36:8:44 | ControlFlowNode for aConstant | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | app_unsafe.py:8:36:8:44 | ControlFlowNode for aConstant | The SECRET_KEY config variable is assigned by $@. | app_unsafe.py:4:13:4:23 | ControlFlowNode for Str | this constant String |
| config2.py:5:14:5:24 | ControlFlowNode for Str | config2.py:5:14:5:24 | ControlFlowNode for Str | config2.py:5:14:5:24 | ControlFlowNode for Str | The SECRET_KEY config variable is assigned by $@. | config2.py:5:14:5:24 | ControlFlowNode for Str | this constant String |
| config.py:12:18:12:26 | ControlFlowNode for aConstant | config.py:7:13:7:23 | ControlFlowNode for Str | config.py:12:18:12:26 | ControlFlowNode for aConstant | The SECRET_KEY config variable is assigned by $@. | config.py:7:13:7:23 | ControlFlowNode for Str | this constant String |
| config.py:17:18:17:47 | ControlFlowNode for Attribute() | config.py:7:13:7:23 | ControlFlowNode for Str | config.py:17:18:17:47 | ControlFlowNode for Attribute() | The SECRET_KEY config variable is assigned by $@. | config.py:7:13:7:23 | ControlFlowNode for Str | this constant String |
| config.py:18:18:18:52 | ControlFlowNode for Attribute() | config.py:7:13:7:23 | ControlFlowNode for Str | config.py:18:18:18:52 | ControlFlowNode for Attribute() | The SECRET_KEY config variable is assigned by $@. | config.py:7:13:7:23 | ControlFlowNode for Str | this constant String |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-287-ConstantSecretKey/WebAppConstantSecretKey.ql

View File

@@ -0,0 +1,22 @@
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'CHANGEME'
Session(app)
@app.route('/')
def index():
if 'logged_in' not in session:
session['logged_in'] = False
if session['logged_in']:
return '<h1>You are logged in!</h1>'
else:
return '<h1>Access Denied</h1>', 403
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,21 @@
from flask import Flask, session
from secrets import token_hex
app = Flask(__name__)
SECRET_KEY = token_hex(16)
@app.route('/')
def index():
if 'logged_in' not in session:
session['logged_in'] = False
if session['logged_in']:
return '<h1>You are logged in!</h1>'
else:
return '<h1>Access Denied</h1>', 403
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,24 @@
from flask import Flask, session
app = Flask(__name__)
aConstant = 'CHANGEME1'
app.config['SECRET_KEY'] = aConstant
app.secret_key = aConstant
app.config.update(SECRET_KEY=aConstant)
app.config.from_mapping(SECRET_KEY=aConstant)
app.config.from_pyfile("config.py")
app.config.from_object('config.Config')
@app.route('/')
def DEB_EX():
if 'logged_in' not in session:
session['logged_in'] = False
if session['logged_in']:
return app.secret_key
else:
return app.secret_key, 403
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,19 @@
"""Flask App configuration."""
from os import environ
import os
import random
FLASK_DEBUG = True
aConstant = 'CHANGEME2'
class Config:
SECRET_KEY = environ.get("envKey")
SECRET_KEY = aConstant
SECRET_KEY = os.getenv('envKey')
SECRET_KEY = os.environ.get('envKey')
SECRET_KEY = os.environ.get('envKey', random.randint)
SECRET_KEY = os.getenv('envKey', random.randint)
SECRET_KEY = os.getenv('envKey', aConstant)
SECRET_KEY = os.environ.get('envKey', aConstant)
SECRET_KEY = os.environ['envKey']

View File

@@ -0,0 +1,5 @@
"""Flask App configuration."""
# General Config
FLASK_DEBUG = True
SECRET_KEY = "CHANGEME5"