https://github.com/henrikac/kemal-shield
A shard that adds an extra layer of protection to Kemal apps by setting various HTTP headers.
https://github.com/henrikac/kemal-shield
crystal crystal-lang crystal-language
Last synced: 11 months ago
JSON representation
A shard that adds an extra layer of protection to Kemal apps by setting various HTTP headers.
- Host: GitHub
- URL: https://github.com/henrikac/kemal-shield
- Owner: henrikac
- License: mit
- Created: 2021-07-03T14:36:30.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2021-07-06T16:11:02.000Z (over 4 years ago)
- Last Synced: 2025-05-12T22:54:42.529Z (11 months ago)
- Topics: crystal, crystal-lang, crystal-language
- Language: Crystal
- Homepage:
- Size: 53.7 KB
- Stars: 9
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# kemal-shield
This is a shard that adds an extra layer of protection to your [Kemal](https://github.com/kemalcr/kemal) app. This is done by setting various HTTP headers.
This shard is inspired by [Helmet](https://github.com/helmetjs/helmet).
## Installation
1. Add the dependency to your `shard.yml`:
```yaml
dependencies:
kemal-shield:
github: henrikac/kemal-shield
```
2. Run `shards install`
## Usage
#### Basic usage
```crystal
require "kemal"
require "kemal-shield"
Kemal::Shield.activate # => adds handlers with their default settings
get "/" do
"Home"
end
Kemal.run
```
#### Managing handlers
##### Add handlers
The handlers can also be added individually as you would add any other handler.
The recommended way of adding handlers is to use `Kemal::Shield.add_handler` instead of Kemal's built-in `add_handler`.
The reason for using `Kemal::Shield.add_handler` over Kemal's built-in `add_handler` is that `Kemal::Shield.add_handler` keeps track of all `Kemal::Shield::Handler`.
It also makes sure that no dublicate `Kemal::Shield::Handler` is being added.
```crystal
Kemal::Shield.add_handler Kemal::Shield::XPoweredBy.new
Kemal::Shield.add_handler Kemal::Shield::XFrameOptions.new("DENY")
```
A `Kemal::Shield::DublicateHandlerError` is raised if a dublicate handler is added.
##### Remove handlers
There are two ways of removing a `Kemal::Shield::Handler`.
1. `Kemal::Shield.remove_handler` if you just need to remove a single handler
2. `Kemal::Shield.deactivate` if you want to remove all `Kemal::Shield::Handler`.
```crystal
Kemal::Shield.remove_handler Kemal::Shield::ExpectCT
```
#### Custom handler
Creating a new handler works just as creating a regular Kemal handler.
The only difference is that the new handlers should inherit from `Kemal::Shield::Handler` instead of `Kemal::Handler`.
```crystal
class CustomHandler < Kemal::Shield::Handler
def call(context)
# code ...
call_next context
end
end
```
This makes it possible to add handlers using `Kemal::Shield.add_handler` and/or remove handlers using `Kemal::Shield.remove_handler` or `Kemal::Shield.deactivate`.
#### Configuration
The different headers can be configured in the same way as Kemal:
```crystal
Kemal::Shield.config do |config|
config.csp_on = true
config.hide_powered_by = true
config.no_sniff = true
config.referrer_policy = ["no-referrer"]
config.x_xss_protection = false
end
```
or
```crystal
Kemal::Shield.config.hide_powered_by = true
```
Configuration should be done before calling `Kemal::Shield.activate`.
This is to make sure that the handlers will use the custom configurations.
```crystal
Kemal::Shield.config.corp = "cross-origin" # => Good: This will be used when calling .activate
Kemal::Shield.activate
Kemal::Shield.config.corp = "same-site" # => Bad: Handlers has already been initialized
```
| Option | Description | Default |
|---|---|---|
| csp_on | Set Content-Security-Policy header | `true` |
| csp_defaults | Add CSP default directives | `true` |
| csp_directives | CSP directives | `DEFAULT_DIRECTIVES` |
| csp_report_only | Set Content-Security-Policy-Report-Only header | `false` |
| coep_on | Set Cross-Origin-Embedder-Policy header | `true` |
| coop_on | Set Cross-Origin-Opener-Policy header | `true` |
| coop | Cross-Origin-Opener-Policy policy | `"same-origin"` |
| corp_on | Set Cross-Origin-Resource-Policy header | `true` |
| corp | Cross-Origin-Resource-Policy policy | `"same-origin"` |
| expect_ct | Set Expect-CT header | `true` |
| expect_ct_max_age | Seconds the user agent should regard the host of the received message as a known Expect-CT host | `0` |
| expect_ct_enforce | Whether the user agent should enforce compliance with the Certificate Transparency policy | `false` |
| expect_ct_report_uri | The URI where the user agent should report Expect-CT failures | `""` |
| hide_powered_by | Whether to remove the X-Powered-By header | `true` |
| no_sniff | Set X-Content-Type-Options header | `true` |
| oac | Set Origin-Agent-Cluster header | `true` |
| referrer_on | Set Referrer-Policy header | `true` |
| referrer_policy | The Referrer-Policy policy | `["no-referrer"]` |
| sts_on | Set Strict-Transport-Security | `true` |
| sts_max_age | Seconds that the browser should remember that a site is only to be accessed using HTTPS | `15_552_000` |
| sts_include_sub | Add rule to subdomains | `true` |
| sts_preload | [Preloading STS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security) | `false` |
| x_dns_prefetch_control_on | Set X-DNS-Prefetch-Control header | `true` |
| x_dns_prefetch_control | Enable DNS prefetching | `false` |
| x_download_options | Set X-Download-Options header | `true` |
| x_frame_options_on | Set X-Frame-Options | `true` |
| x_frame_options | X-Frame-Options directive | `"SAMEORIGIN"` |
| x_permitted_cross_domain_policies_on | Set X-Permitted-Cross-Domain-Policies header | `true` |
| x_permitted_cross_domain_policies | X-Permitted-Cross-Domain-Policies directive | `none` |
| x_xss_protection | Enable X-XSS-Protection header | `false` |
`ContentSecurityPolicy::DEFAULT_DIRECTIVES`:
```
default-src 'self';
base-uri 'self';
block-all-mixed-content;
font-src 'self' https: data:;
frame-ancestors 'self';
img-src 'self' data:;
object-src 'none';
script-src 'self';
script-src-attr 'none';
style-src 'self' https: 'unsafe-inline';
upgrade-insecure-requests;
```
#### Handlers
+ `ContentSecurityPolicy`
+ `CrossOriginEmbedderPolicy`
+ `CrossOriginOpenerPolicy`
+ `CrossOriginResourcePolicy`
+ `ExpectCT`
+ `OriginAgentCluster`
+ `ReferrerPolicy`
+ `StrictTransportSecurity`
+ `XContentTypeOptions`
+ `XDNSPrefetchControl`
+ `XDownloadOptions`
+ `XFrameOptions`
+ `XPermittedCrossDomainPolicies`
+ `XPoweredBy`
+ `XXSSProtection`
## Contributing
1. Fork it ()
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
## Contributors
- [Henrik Christensen](https://github.com/henrikac) - creator and maintainer