Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft for review of security for plugin developers #4701

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions content/doc/book/security/_chapter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ sections:

# New material to be integrated after reorg
- credentials

- controller-isolation/required-role-check
3 changes: 3 additions & 0 deletions content/doc/book/security/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@ link:user-content[Rendering User Content]::
By default, Jenkins strictly limits the features useable in user content (files from workspaces, archived artifacts, etc.) it serves.
This chapter discusses how to customize this and make HTML reports and similar content both functional and safe to view. +
_This is set up securely by default._

Information about security practices that are required from plugin developers and maintainters is in
link:/doc/developer/security/[Security for Plugin Developers].
75 changes: 75 additions & 0 deletions content/doc/developer/security/:
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: Security for Plugin Developers
layout: developerchapter
wip: true
references:
- url: https://plugins.jenkins.io/script-security#ScriptSecurityPlugin-Developer%E2%80%99sguide
title: Script Security Developer's Guide
---

Developers and maintainers of plugins play a crucial role in maintaining Jenkins security.
This chapter discusses the security practices required.

Monitor security advisories::

Monitor Jenkins
link:https://www.jenkins.io/security/advisories/[Security Advisories]
closely.
It may be necessary to modify your plugin to work and comply with security fixes.

Conform to access permissions::

Understand and conform to the
link:/doc/developer/security/security-architecture/[Security Architecture of Jenkins].
Specifically:

* Ensure that your code checks the ACL before performing a security-sensitive operation.
* Use the `StaplerProxy` interface to control read access to `AccessControlled` objects.

Store user credentials as secrets::

Protect user credentials by storing them on disk in a field of type `Secret`
and never in a simple `String` field.
Use a getter that returns the same type to access the `Secrets` field
from other code.
See
link:/doc/developer/security/secrets/[Storing Secrets]
for background information, instructions, and code examples.

Implement appropriate script security::

Be sure that your plugin implements appropriate security
for custom Groovy scripts that users may need to create to customize Jenkins.

For more information, see the _Developer's Guide_ section of the
link:https://plugins.jenkins.io/script-security/[Script Security] documentation.

Provide Role Check for Callable::

Communication between the Jenkins controller and agents is implemented with the Java
link:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html[Callable] interface.
Plugins should always implement a _role check_ that runs after a
`Callable` message to ensure that the object executes on the proper side of the controller-agent communication.
Jenkins 2.319 and Jenkins LTS 2.303.3 and later releases enforce this behavior.
A plugin that does not comply throws a `SecurityException` and logs an error message.

See
link:/doc/developer/security/remoting-callables/[Remoting Callables] and
link:/doc/book/security/controller-isolation/required-role-check/[Required Role Check]
for more information.

Protect form validation methods from unauthorized accesss::

Form validation code can be used to compromise private information.
To protect against this:

* Use a call to `Jenkins#checkPermission` to form validation code.

* Only accept `POST` requests for form validation code with side effects for plugins that run on current Jenkins releases.
This protects your plugin from cross-site request forgery (CSRF) attacks.

* Forms in plugins that run on older Jenkins releases may be sent using `GET` and need to use the `checkMethod` attribute.

////
https://wiki.jenkins.io/display/JENKINS/Making+your+plugin+behave+in+secured+Jenkins
////
1 change: 1 addition & 0 deletions content/doc/developer/security/_chapter.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
sections:
- security-architecture
- secrets
guides:
- form-validation
Expand Down
43 changes: 23 additions & 20 deletions content/doc/developer/security/form-validation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ public FormValidation doCheckFoo(@QueryParameter String value) {
}
----

Stapler routing and Jenkins behavior will result in this method being routed at a URL like `/descriptorByName/f.q.ClassName/checkFoo`.
This URL will contain a minimal HTML snippet with a form validation message (if present) based on the presence and value of the `value` query parameter.
Stapler routing and Jenkins behavior result in this method being routed at a URL like `/descriptorByName/f.q.ClassName/checkFoo`.
This URL contains a minimal HTML snippet with a form validation message (if present) based on the presence and value of the `value` query parameter.

=== Potential Security Issues
== Potential Security Issues

==== Returning Private Information
=== Returning Private Information

By default, any user with Overall/Read access can access form validation methods like the example method above.
Some form validation methods could be used to infer private information, and as such may need to be protected from unauthorized access.

If a form validation method is supposed to only be accessible to users with specific permissions, a permission check should be added.

==== Side Effects
=== Side Effects

Form validation can become quite complex and have side effects.
For example, Jenkins might evaluate a Groovy script passed as parameter, or access a URL passed as parameter.
Expand All @@ -48,9 +48,9 @@ Ensuring that form validation code such as this is protected with appropriate pe
link:https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross-site request forgery] is an attack on users with the necessary privileges and exploits the trust Jenkins has in these legitimate users' web browsers.
Protecting from these attacks requires that form validation methods with side effects only accept `POST` requests.

=== Implementing Form Validation
== Implementing Form Validation

==== Checking permissions
=== Checking permissions

To check for global permissions like `Administer`, add a call to `Jenkins#checkPermission`. Note that Overall/Read permission is always implied (unless you specifically implement jenkinsdoc:UnprotectedRootAction[]).

Expand Down Expand Up @@ -82,18 +82,20 @@ public FormValidation doCheckFoo(@QueryParameter String value, @AncestorInPath I
}
----

==== Protecting from CSRF
=== Protecting from CSRF

Stapler web methods, like form validation, can be invoked using any HTTP verb by default.
This can be used by malicious users to attack an application by way of its legitimate users:
Cross-site request forgery is the result of a web application (Jenkins) trusting legitimate users' browser.
Cross-site request forgery is the result of a web application (Jenkins) trusting a legitimate user's browser.

To prevent this, Jenkins includes CSRF protection that requires actions with side effects (POST requests) to submit a user-specific secret, called a _crumb_.
To ensure this protection is relevant, web methods with side effects need to reject requests not sent via POST.
To prevent this, Jenkins includes CSRF protection that requires actions with side effects (`POST` requests) to submit a user-specific secret, called a _crumb_.
To ensure that this protection is relevant, web methods with side effects need to reject requests not sent via `POST`.
There are two options to achieve this in general:

* staplerdoc:org.kohsuke.stapler.verb.POST[`POST`] limits processing to just the POST verb, and all other verbs will receive a 404 response. This is recommended approach for form validation methods.
* staplerdoc:org.kohsuke.stapler.interceptor.RequirePOST[`RequirePOST`] is the older (and more common) approach. It will show a form allowing users to resubmit the request using POST if they used a different verb. This is mostly useful for simple API actions.
* staplerdoc:org.kohsuke.stapler.verb.POST[`POST`] limits processing to just the POST verb, and all other verbs receive a 404 response. This is the recommended approach for form validation methods.
* staplerdoc:org.kohsuke.stapler.interceptor.RequirePOST[`RequirePOST`] is the older (and more common) approach.
It shows a form that allows users to resubmit the request using `POST` if they used a different verb.
This is mostly useful for simple API actions.

Applying this protection is as simple as adding the annotation to the method:

Expand All @@ -108,8 +110,8 @@ public FormValidation doCheckFoo(@QueryParameter String value) {
}
----

Depending on the versions of Jenkins supported by your plugin, these form validation methods may be invoked using HTTP GET, however, so the form may need to be adapted as well.
The Jenkins form Jelly controls support the `checkMethod` attribute, which, if set to `post`, will result in form validation being invoked via HTTP POST.
Depending on the versions of Jenkins supported by your plugin, however, these form validation methods may be invoked using `HTTP GET`, so the form may need to be adapted as well.
The Jenkins form Jelly controls support the `checkMethod` attribute, which, if set to `post`, results in form validation being invoked via `HTTP POST`:

[source, xml]
----
Expand All @@ -118,11 +120,12 @@ The Jenkins form Jelly controls support the `checkMethod` attribute, which, if s
</f:entry>
----

The default behavior of form controls has changed over time, so you may not need to add the `checkMethod` attribute depending on the versions of Jenkins supported by your plugin and the types of form controls that have validation:
The default behavior of form controls has changed over time, so you may not need to add the `checkMethod` attribute, depending on the versions of Jenkins supported by your plugin and the types of form controls that have validation:

* Historically, form validation requests were sent using GET by default (with the exception of `f:validateButton`, which always used POST).
* Historically, form validation requests were sent using `GET` by default (with the exception of `f:validateButton`, which always used `POST`).
* Since Jenkins 2.84 and 2.73.3, `f:password` always sends form validation as `POST`.
* Since Jenkins 2.285, most other form controls send form validation requests as `POST` by default, and `checkMethod` would only opt out.
* As of Jenkins 2.300 `f:checkbox` does not support the `checkMethod` attribute at all and requests are always sent using GET.
* Since Jenkins 2.285, most other form controls send form validation requests as `POST` by default, and `checkMethod` only opts out.
* As of Jenkins 2.300 `f:checkbox` does not support the `checkMethod` attribute at all and requests are always sent using `GET`.

NOTE: Some basic form controls may not declare the `checkMethod` attribute in older versions of Jenkins. Depending on the control, it may still work, despite an error shown in your IDE.

NOTE: Some basic form controls may not declare the `checkMethod` attribute in older versions of Jenkins. Depending on the control it could still work, despite an error shown in your IDE.
89 changes: 48 additions & 41 deletions content/doc/developer/security/index.adoc
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
---
title: Security
title: Security for Plugin Developers
layout: developerchapter
wip: true
references:
- url: https://plugins.jenkins.io/script-security#ScriptSecurityPlugin-Developer%E2%80%99sguide
title: Script Security Developer's Guide
---

:imagesdir: /doc/developer/security/resources
Developers and maintainers of plugins play a crucial role in maintaining Jenkins security.
This chapter discusses the security practices required.

// this is a straight import of https://wiki.jenkins.io/display/JENKINS/Making+your+plugin+behave+in+secured+Jenkins
// TODO check contents and remove wiki page
Monitor security advisories::

Monitor Jenkins
link:https://www.jenkins.io/security/advisories/[Security Advisories]
closely.
It may be necessary to modify your plugin to work and comply with security fixes.

== Security Architecture of Jenkins
Conform to access permissions::

Jenkins has a security mechanism in place so that the administrator of Jenkins can control who gets access to what part of Jenkins.
The key components of this mechanism are the followings:
Understand and conform to the
link:/doc/developer/security/security-architecture/[Security Architecture of Jenkins].
Specifically:

* jenkinsdoc:Permission[], which represents an activity that requires a security privilege.
This is usually a verb, like "configure", "administer", "tag", etc.
* `Authentication`, which represents the current user and roles (AKA groups) he/she has.
When a thread runs in Jenkins, it always carry an `Authentication` object implicitly, which represents the user that the thread is serving. (If a thread is a part of Jenkins and not serving any user request, like `Executor{`}s, then it carries an almighty "system" `Authentication` object.)
* jenkinsdoc:ACL[], which decides whether the `Authentication` object carried by the current thread has the given permission or not.
* jenkinsdoc:AccessControlled[], which is implemented by an object who owns ACL.
* Ensure that your code checks the ACL before performing a security-sensitive operation.
* Use the `StaplerProxy` interface to control read access to `AccessControlled` objects.

So the overall picture is this; various objects in Jenkins (such as jenkinsdoc:Job[], jenkinsdoc:Jenkins[], jenkinsdoc:User[], jenkinsdoc:View[], etc.) are jenkinsdoc:AccessControlled[] objects, and therefore they own ACLs.
The code is then written in such a way that before a security-sensitive operation is performed, it checks ACL.
Store user credentials as secrets::

For example, the following code is taken from the jenkinsdoc:Jenkins[] class, which lets you shut down the JVM by requesting `/exit`.
You can easily imagine that in a security sensitive environment you don't want random users to invoke this, so it makes sure that the caller has the "ADMINISTER" permission of the system before proceeding to do the work:
Protect user credentials by storing them on disk in a field of type `Secret`
and never in a simple `String` field.
Use a getter that returns the same type to access the `Secrets` field
from other code.
See
link:/doc/developer/security/secrets/[Storing Secrets]
for background information, instructions, and code examples.

[source,java]
----
Expand All @@ -44,35 +49,34 @@ You can easily imagine that in a security sensitive environment you don't want r
w.println("Shutting down");
}
}

System.exit(0);
}
----
<1> This throws an exception if the user accessing this URL doesn't have `Administer` permission.

If the administrator configured no security mechanism, the checkPermission method simply becomes no-op.
The administrator could configure matrix-based ACL, in which case every `AccessControlled` object will share the single ACL (whose contents is controlled by the configuration done by the administrator.) In more elaborate case, each `AccessControlled` object might have different ACLs.
In all cases, this is the code you need to write.
Implement appropriate script security::

Be sure that your plugin implements appropriate security
for custom Groovy scripts that users may need to create to customize Jenkins.

== Controlling read access to `AccessControlled` objects
For more information, see the _Developer's Guide_ section of the
link:https://plugins.jenkins.io/script-security/[Script Security] documentation.

The recommended way to control read access to `AccessControlled` objects is to implement the `StaplerProxy` interface.
See link:read-access[Restricting HTTP Access to `AccessControlled` Objects] for more information.
Provide Role Check for Callable::

== What do plugins need to do to protect web methods?
Communication between the Jenkins controller and agents is implemented with the Java
link:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html[Callable] interface.
Plugins should always implement a _role check_ that runs after a
`Callable` message to ensure that the object executes on the proper side of the controller-agent communication.
Jenkins 2.319 and Jenkins LTS 2.303.3 and later releases enforce this behavior.
A plugin that does not comply throws a `SecurityException` and logs an error message.

* Identify the operations in code that can be potentially security sensitive.
This includes anything that can change state in the server, have other side effects (like elaborate form validation `doCheck` methods), or potentially discloses protected information (like auto-completion `doAutoComplete` methods).
These methods should perform `checkPermission`.
* Identify the nearest `AccessControlled` objects to check permissions with.
If your 'this' object is already access-controlled, then that's obviously it.
Otherwise, try to look for the nearest logical ancestor.
If all else fails, use the `Jenkins` singleton.
* Identify the `Permission` object to use.
If you extend from an `ExtensionPoint`, it might already define some permission objects as public static final fields in them.
If you are defining a sub-system of a substantial size, you might want to create new `Permission` objects (see the end of the `View` class for this example.) If you don't know, you can use `Jenkins.ADMINISTER` as a starting point.
See
link:/doc/developer/security/remoting-callables/[Remoting Callables] and
link:/doc/book/security/controller-isolation/required-role-check/[Required Role Check]
for more information.

With these three information, you can now insert:
Protect form validation methods from unauthorized accesss::

Form validation code can be used to compromise private information.
To protect against this use a call to `Jenkins#checkPermission` to form validation code:

[source]
----
Expand All @@ -81,9 +85,12 @@ Permission p = ... do the step 3 above ...
ac.checkPermission(p)
----

See also link:form-validation[Securely implementing form validation].
* Only accept `POST` requests for form validation code with side effects for plugins that run on current Jenkins releases.
This protects your plugin from cross-site request forgery (CSRF) attacks.

* Forms in plugins that run on older Jenkins releases may be sent using `GET` and need to use the `checkMethod` attribute.

=== Checking permissions in Jelly files
See link:/doc/developer/security/form-validation/#returning-private-information[Securely implementing form validation] for details.

If your entire HTML page rendered by Jelly needs to be protected, you can use the attributes of the `<l:layout>` tag, like this:

Expand Down
Loading