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

Add security setting to more strictly enforce audience validation #622

Merged
merged 1 commit into from
Oct 14, 2021
Merged
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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,29 @@ validation fails. You may disable such exceptions using the `settings.security[:
settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
```

#### Audience Validation

A service provider should only consider a SAML response valid if the IdP includes an <AudienceRestriction>
element containting an <Audience> element that uniquely identifies the service provider. Unless you specify
the `skip_audience` option, Ruby SAML will validate that each SAML response includes an <Audience> element
whose contents matches `settings.sp_entity_id`.

By default, Ruby SAML considers an <AudienceRestriction> element containing only empty <Audience> elements
to be valid. That means an otherwise valid SAML response with a condition like this would be valid:

```xml
<AudienceRestriction>
<Audience />
</AudienceRestriction>
```

You may enforce that an <AudienceRestriction> element containing only empty <Audience> elements
is invalid using the `settings.security[:strict_audience_validation]` parameter.

```ruby
settings.security[:strict_audience_validation] = true
```

#### Key Rollover

To update the SP X.509 certificate and private key without disruption of service, you may define the parameter
Expand Down
7 changes: 6 additions & 1 deletion lib/onelogin/ruby-saml/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,12 @@ def validate_in_response_to
#
def validate_audience
return true if options[:skip_audience]
return true if audiences.empty? || settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
return true if settings.sp_entity_id.nil? || settings.sp_entity_id.empty?

if audiences.empty?
return true unless settings.security[:strict_audience_validation]
return append_error("Invalid Audiences. The <AudienceRestriction> element contained only empty <Audience> elements. Expected audience #{settings.sp_entity_id}.")
end

unless audiences.include? settings.sp_entity_id
s = audiences.count > 1 ? 's' : '';
Expand Down
3 changes: 2 additions & 1 deletion lib/onelogin/ruby-saml/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ def get_binding(value)
:digest_method => XMLSecurity::Document::SHA1,
:signature_method => XMLSecurity::Document::RSA_SHA1,
:check_idp_cert_expiration => false,
:check_sp_cert_expiration => false
:check_sp_cert_expiration => false,
:strict_audience_validation => false,
}.freeze
}.freeze
end
Expand Down
12 changes: 10 additions & 2 deletions test/response_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -490,14 +490,22 @@ def generate_audience_error(expected, actual)
assert_empty response.errors
end

it "return true when the audience is self closing" do
it "return true when the audience is self closing and strict audience validation is not enabled" do
response_audience_self_closed.settings = settings
response_audience_self_closed.settings.sp_entity_id = '{audience}'
assert response_audience_self_closed.send(:validate_audience)
assert_empty response_audience_self_closed.errors
end

it "return false when the audience is valid" do
it "return false when the audience is self closing and strict audience validation is enabled" do
response_audience_self_closed.settings = settings
response_audience_self_closed.settings.security[:strict_audience_validation] = true
response_audience_self_closed.settings.sp_entity_id = '{audience}'
refute response_audience_self_closed.send(:validate_audience)
assert_includes response_audience_self_closed.errors, "Invalid Audiences. The <AudienceRestriction> element contained only empty <Audience> elements. Expected audience {audience}."
end

it "return false when the audience is invalid" do
response.settings = settings
response.settings.sp_entity_id = 'invalid_audience'
assert !response.send(:validate_audience)
Expand Down