• Threat model at speed,
    and scale.

  • Engage & educate developers.
    Fit into their workflows.

  • Generate reports,
    from code.

Continuous threat modelling, through code.

ThreatSpec is an open source project, and our mission is to do for security what unit testing and TDD has done for development.

Write code

Developers focus on doing what they do best: writing awesome code. And don't forget, when everything is code, everyone's a developer!

Annotate code

Developers and QA etc. add threat model comments to the source code, describing security-relevant decisions, concerns, and everything in between.

Peer review

The code and threat model comments are peer reviewed as usual. Security should be involved in the review process, even contributing changes back to developers.

Review dynamic reports

High and low-level threat model reports are automatically generated directly from code. Use CI/CD to generate and publish the reports.

Generate data flow diagrams

Automatically generate DFDs every time the code changes. Use these to drive further threat modelling activity, or even display them in the SOC.

Tighten feedback loops

Peer review and shared language encourages close collaboration between developers and security, helping to identify and fix security issues sooner.

Getting Started

Step-by-step threat modelling using ThreatSpec

In this quick tutorial we're going to go step-by-step through an example threat modelling process, using the pythreatspec universal parser as well as a repoting tool to generate a Data-flow Diagram (DFD).

For more information on using ThreatSpec take a look at the wiki: https://github.com/threatspec/threatspec/wiki.

Installation

You'll need Python

Make sure you have python, pip and virtualenv installed. See https://www.python.org/ for details.

You'll also need node.js

We're going to use a tool called mermaid to generate the DFD images. You can install node here: https://nodejs.org/en/.

Create a threatspec directory

This is where we will be spending most of our time. It has the tutorial files as well as the universal parser script.

We're going to be installing a few scripts, so let's create ourselves somewhere to keep all of the ThreatSpec directories. All of the below commands will take place in that directory.

$ mkdir $HOME/threatspec
$ cd $HOME/threatspec

Install pythreatspec

Clone the GitHub repo and install.

$ git clone https://github.com/threatspec/pythreatspec.git
...
$ pushd pythreatspec
$ virtualenv venv
$ source venv/bin/activate
$ python setup.py install
$ deactivate
$ popd

Install report_dfd

This tool will take the output of ThreatSpec and turn it into a file that mermaid can understand. Mermaid will then turn that into a diagram.

Clone the GitHub repo and install.

$ git clone https://github.com/threatspec/report_dfd.git
...
pushd report_dfd
$ virtualenv venv
$ popd

Install mermaid

Follow the instructions here https://knsv.github.io/mermaid/#usage.

Step 0 - LAMP_Multi_AZ_00_base.py

In this tutorial we are going to threat model a web application infrastructure stack that has been written as a troposphere Python script. The troposphere script creates a Amazon Web Services CloudFormation Template. See here for more details: https://aws.amazon.com/cloudformation/.

Inside the pythreatspec repository is the tutorial directory containing all the required files, so let's go there.

$ pushd pythreatspec
$ cd tutorial

In this directory you'll see a helper script called run_tutorial_step.sh, we'll use this to run each step. For information on how to use the parser itself, take a look at the pythreatspec code repository.

The file we'll be starting with is LAMP_Multi_AZ_00_base.py, so take a look at the file in your favourite editor. You should see lots of interesting stuff like:

WebServerSecurityGroup = t.add_resource(SecurityGroup(
    "WebServerSecurityGroup",
    SecurityGroupIngress=[{ "ToPort": "80", "IpProtocol": "tcp", "SourceSecurityGroupOwnerId": GetAtt("ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"), "SourceSecurityGroupName": GetAtt("ElasticLoadBalancer", "SourceSecurityGroup.GroupName"), "FromPort": "80" }, { "ToPort": "22", "IpProtocol": "tcp", "CidrIp": Ref(SSHLocation), "FromPort": "22" }],
    GroupDescription="Enable HTTP access via port 80 locked down to the ELB and SSH access",
))

Step 1 - LAMP_Multi_AZ_01_components.py

As you read through the script, you probably noticed a few key things happening, like the definition of major components that make up our web application stack. In this step we're going to go through the source file and document the interesting components as we find them. You'll find the results of this step in the LAMP_Multi_AZ_01_components.py file, including bits like:

from troposphere.rds import DBInstance

# Not sure about boundaries yet, so define a global one for now
# @alias boundary @app to App

t = Template()

and

# @alias component @app:@web_group to WebServerASGGroup
WebServerGroup = t.add_resource(AutoScalingGroup(
    "WebServerGroup",
    DesiredCapacity=Ref(WebServerCapacity),
    LaunchConfigurationName=Ref("LaunchConfig"),
    MinSize="1",
    MaxSize="5",
    LoadBalancerNames=[Ref("ElasticLoadBalancer")],
    AvailabilityZones=GetAZs(""),
))

As you can see we're using an @alias tag to first define an App boundary, then to define a WebServerASGGroup component amongst others. The @alias tag allows you to create a short identifer that can be reused across source files and even different projects. It is defined as follows:

@alias boundary <@boundary_id> to <name>
@alias component <@boundary_id>:<@component_id> to <name>
@alias threat <@threat_id> to <name>

We'll now use the run_tutorial_step.sh script to parse the source file and generate the Data-flow diagram.

$ ./run_tutorial_step.sh LAMP_Multi_AZ_01
Running universal parser for LAMP_Multi_AZ_01
2017-05-14T22:30:05 INFO: Parsing file LAMP_Multi_AZ_01_components.py
2017-05-14T22:30:05 INFO: Writing output to LAMP_Multi_AZ_01.threatspec.json
Running mermaid DFD reporting tool
...
CONSOLE: %c 21:30:06 (671) :%cDEBUG:  color:grey; color: green; Drawing flowchart (from line # in "")
saved png: LAMP_Multi_AZ_01.mermaid.png
You can now view LAMP_Multi_AZ_01.mermaid.png

If you open the file LAMP_Multi_AZ_01.mermaid.png you should see something a bit like this:



Step 2 - LAMP_Multi_AZ_02_boundaries.py

Looking at the components, we can clearly see that there are two main types of components, namely Web and Database. These should make good starting points for the trust boundaries.

We'll remove the @alias for the App boundary, and we'll add in other boundaries where appropriate. For example:

# @alias component @web:@elb to ElasticLoadBalancer
# Who uses the ELB? Well, a user, so adding them in
# @alias boundary @external to External
# @alias component @external:@user to User
ElasticLoadBalancer = t.add_resource(LoadBalancer(
    "ElasticLoadBalancer",
    HealthCheck=HealthCheck(
        HealthyThreshold="2",
        Interval="10",
        Target="HTTP:80/",
        Timeout="5",
        UnhealthyThreshold="5",
    ),
    LBCookieStickinessPolicy=[{ "PolicyName": "CookieBasedPolicy", "CookieExpirationPeriod": "30" }],
    CrossZone="true",
    Listeners=[{ "InstancePort": "80", "PolicyNames": ["CookieBasedPolicy"], "LoadBalancerPort": "80", "Protocol": "HTTP" }],
    AvailabilityZones=GetAZs(""),
))

We'll run the run_tutorial_step.sh file again.

$ ./run_tutorial_step.sh LAMP_Multi_AZ_02
...
You can now view LAMP_Multi_AZ_02.mermaid.png

This gives us:



Step 3 - LAMP_Multi_AZ_03_connections.py

So far so good, but all we have at the moment is a collection of disconnected components grouped into various trust boundaries. The troposphere script specifies different relationships between the components, so we'll use the @connects tag to sketch out those connections. We've added them to the file LAMP_Multi_AZ_03_connections.py, so you should see things like:

# @alias component @db:@db_ec2_sg to DBEC2SecurityGroup
# @connects @db:@db_sg with @db:@db_ec2_sg
# @connects @web:@web_sg to @db:@db_ec2_sg as mysql tcp/3306
DBEC2SecurityGroup = t.add_resource(SecurityGroup(
    "DBEC2SecurityGroup",
    SecurityGroupIngress=[{ "ToPort": "3306", "IpProtocol": "tcp", "SourceSecurityGroupName": Ref(WebServerSecurityGroup), "FromPort": "3306" }],
    GroupDescription="Open database for access",
    Condition="Is-EC2-VPC",
))

The @connects tag can be used to create uni-directional (to) and bi-directional (with) connections and is defined as:

@connects <@boundary_id>:<@component_id> to <@boundary_id>:<@component_id> [as <name>]
@connects <@boundary_id>:<@component_id> with <@boundary_id>:<@component_id> [as <name>]

We'll run the run_tutorial_step.sh file once again.

$ ./run_tutorial_step.sh LAMP_Multi_AZ_03
...
You can now view LAMP_Multi_AZ_03.mermaid.png

This gives us:



Step 4 - LAMP_Multi_AZ_04_threats.py

Now things are really getting interesting. The above image gives us a pretty good idea of what components there are and how they're connected. We're now in a position to start thinking about the threats against the system.

In this example we're going to use the SANS Top 25 (https://uk.sans.org/top25-software-errors/) as a way to guide our threat modelling effort. A lot of the CWEs aren't relevent to our current infrastructure-level perspective, but the section titled Porous Defenses does have relevant threats. Take CWE-311 Missing Encryption of Sensitive Data for example. Thinking about this threat as we look at our Data-flow diagram, one thing immediately stands out. The users are connecting to the Elastic Loadbalancer using http, not https. This is bad. Even if we're not handling credit cards or other very sensitive data, it is still best practise to encrypt all traffic. We may ask the user to log in, so we need to protect their credentials and session details. We may ask them to sign up with personal information, so we should ensure this is protected as well. Or perhaps our service is of a religious, political or sexual nature and we want to ensure our users' right to privacy.

So, let's add a note about CWE-311. In the pythreatspec examples directory you'll see a file called cwe_library.threatspec.json. This was created from the CWE xml file provided by Mitre which was processed by the cwe_to_threatspec.py script. If we look for @cwe_311 in the json file, we'll find the alias identifier that we can use in our source code.

 $ grep @cwe_311 ../examples/cwe_library.threatspec.json
    "@cwe_311_missing_encryption_of_sensitive_data": {

We can now use the @cwe_311_missing_encryption_of_sensitive_data identifier. In this case, because we've exposed ourselves to CWE-311, we're going to use the @exposes tag, so let's add it.

# @alias component @web:@elb to ElasticLoadBalancer
# Who uses the ELB? Well, a user, so adding them in
# @alias boundary @external to External
# @alias component @external:@user to User
# @connects @external:@user to @web:@elb as http tcp/80
# @exposes @web:@elb to @cwe_311_missing_encryption_of_sensitive_data with lack of TLS
ElasticLoadBalancer = t.add_resource(LoadBalancer(
    "ElasticLoadBalancer",
    HealthCheck=HealthCheck(
        HealthyThreshold="2",
        Interval="10",
        Target="HTTP:80/",
        Timeout="5",
        UnhealthyThreshold="5",
    ),
    LBCookieStickinessPolicy=[{ "PolicyName": "CookieBasedPolicy", "CookieExpirationPeriod": "30" }],
    CrossZone="true",
    Listeners=[{ "InstancePort": "80", "PolicyNames": ["CookieBasedPolicy"], "LoadBalancerPort": "80", "Protocol": "HTTP" }],
    AvailabilityZones=GetAZs(""),
))

We can go through each of the CWEs in the SANS Top 25 and think about how they apply to our system components. As another example, consider the use of SSH on port 22.

# @alias boundary @mgmt to Management
# @alias component @mgmt:@admin to Administrator
# @alias boundary @web to Web
# @alias component @web:@web_sg to WebServerSecurityGroup
# @connects @mgmt:@admin to @web:@web_sg as ssh tcp/22
# @mitigates @web:@web_sg against @cwe_306_missing_authentication_for_critical_function with use of secure shell
# @mitigates @web:@web_sg against @cwe_311_missing_encryption_of_sensitive_data with use of secure shell
# @connects @web:@elb to @web:@web_sg as http tcp/80
WebServerSecurityGroup = t.add_resource(SecurityGroup(
    "WebServerSecurityGroup",
    SecurityGroupIngress=[{ "ToPort": "80", "IpProtocol": "tcp", "SourceSecurityGroupOwnerId": GetAtt("ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"), "SourceSecurityGroupName": GetAtt("ElasticLoadBalancer", "SourceSecurityGroup.GroupName"), "FromPort": "80" }, { "ToPort": "22", "IpProtocol": "tcp", "CidrIp": Ref(SSHLocation), "FromPort": "22" }],
    GroupDescription="Enable HTTP access via port 80 locked down to the ELB and SSH access",
))

There are four threat related tags, namely @mitigates, @exposes, @transfers and @accepts. These are defined as:

@mitigates <@boundary_id>:<@component_id> against (<@threat_id>|<threat>) with <mitigation>
@exposes <@boundary_id>:<@component_id> to (<@threat_id>|<threat>) with <exposure>
@transfers (<@threat_id>|<threat>) to <@boundary_id>:<@component_id> with <transfer>
@accepts (<@threat_id>|<threat>) to <@boundary_id>:<@component_id> with <acceptance>

There are also two additional tags that can be used. @review lets you make an unstructured note of something that needs further consideration, and @describe lets you add a longer description to a boundary, component or threat. These are defined as:

@review <@boundary_id>:<@component_id> <review>
@describe boundary <@boundary_id> as <description>
@describe component <@boundary_id>:<@component_id> as <description>
@describe threat <@threat_id> as <description>

For more information, read the specifications here: https://github.com/threatspec/threatspec/wiki/Specifications.

We'll now run the run_tutorial_step.sh file for the final time.

$ ./run_tutorial_step.sh LAMP_Multi_AZ_04
...
You can now view LAMP_Multi_AZ_04.mermaid.png

So finally we have a Data-flow diagram showing not only the system components in their trust boundaries, but also relevant exposures and mitigations of threats.



Step 5 - Next steps

This tutorial really only scratched the surface of threat modelling using ThreatSpec. If you want to continue with this example, you could start by adding some additional context. How exactly is the CloudFormation template deployed, where do those database parameters come from? What other threats can you think of? Here are some suggestions for what to do next:

Frequently Ask Questions

What is ThreatSpec?

ThreatSpec is an open source project that aims to close the gap between development and security by bringing the threat modelling process further into the development process. This is achieved by having developers and security engineers write threat specifications alongside code, then dynamically generating reports and data-flow diagrams from the code.

Security testing is shifting left, from annual pentests to the realm of unit testing and test-driven development, by taking advantage of automation and agile practices. ThreatSpec is an attempt to continue the evolution.

Is ThreatSpec free?

Yes! ThreatSpec code and tools are open source, so they're free to use, modify and distribute. The source code is available on Github.

How do I contribute or help out?

We're glad you asked!

Drop us an email to [email protected] or send a tweet to @ThreatSpec.

Anything you can do to help would be awesome. In particular, the following would be super helpful:

  • People to test TreatSpec, giving us feedback
  • Developers interested in adding support for more languages
  • Front-end devs to fix this site (it sucks)
  • People to spread the word, write documentation etc.

What is threat modelling?

Threat modeling is a process by which potential threats can be identified, enumerated, and prioritized – all from a hypothetical attacker’s point of view. The purpose of threat modeling is to provide defenders with a systematic analysis of the probable attacker’s profile, the most likely attack vectors, and the assets most desired by an attacker. Threat modeling answers the questions “Where are the high-value assets?” “Where am I most vulnerable to attack?” “What are the most relevant threats?” “Is there an attack vector that might go unnoticed?” [Wikipedia]

For a fully comprehensive introduction to threat modelling, check out the book Threat Modeling: Designing for Security by Adam Shostack.

What is code-driven threat modelling?

Threat modelling has traditionally been driven by the security team, either using general office tools like diagram drawing programs and spreadsheets, or using specialised threat modelling software. They'd either do it in isolation, based on architecture diagrams etc, or would have meetings with developers where they'd do threat modelling together at the beginning of the release.

ThreatSpec was created out of a need to threat model an open source security tool, where developers were distributed around the world in different time zones. Workflows were all centered around code, so it made sense to focus the threat modelling efforts around code, fitting into existing developer workflows. We wanted the threat model information to live along side the code, growing and changing with it. This way the threat model always stays in sync with the code, no matter how many MVPs or pivots there are.

We also wanted to create a continuous two-way conversation between developers and security. By having developers annotate their code with security information, we would be encouraging them to think about the security implications of their decisions as they went. We also wanted security to be in a position to peer review the changes in real time, taking advantage of code review systems and Continuous Integration / Continuous Deployment (CI/CD) tools.

And most importantly, we wanted security to be able to easily provide fast feedback to developers so that developers could benefit from continuous learning.

Are there any alternatives?

Many people just use whiteboards and standard office tools such as word processors, spreadsheets and diagram drawing applications. However, there are some threat modelling tools out there:

If you know of any more, let us know and we'll add them.