I'm adding CSRF protection to my web site (implemented with Scala Play 2.8). It appears to be working from the browser. That is, I can display a protected page in a browser, change the value of the CSRF token using the browser's tools, submit, and I then get the error message generated by Play at line 131 in CSRFActions.scala.
The problem is how to set up a test to verify this in my test suite.
Here's my test:
class AsisCtrlUpdatePrefsSpec extends OatSpec with OneAppPerSuite {
implicit override lazy val app: FakeApplication = FakeApplication(
withGlobal = Some(oat.wapp.GlobalWApp))
"Getting a preferences form should" - {
"detect CSRF tampering" in new DefaultTestContext("myUser") {
// Get a valid token; create a tampered version
val token = CSRF.SignedTokenProvider.generateToken
val tamperedToken = token.replace("1", "2").replace("3", "4")
assert(token != tamperedToken)
// Build a request object with info to get it authenticated,
// valid data for the posted form, and the tampered csrfToken.
// Include a session with the correct csrfToken
implicit val postRequest = FakeRequest(POST,
routes.AsisCtrl.updatePreferences("advisor").url)
.withAuthenticator(this.identity.loginInfo)
.withFormUrlEncodedBody("col1Prefixes" → "CS", "col2Prefixes" → "MATH",
"acadRecordSort" → "Ascending", "notesSort" → "Ascending",
"csrfToken" → tamperedToken)
.withSession("csrfToken" → token)
// This is what the application should do when a VALID
// token is presented. But with the invalid token, this
// should fail. It doesn't.
val result2 = this.asisCtrl.updatePreferences("advisor")(postRequest)
assertResult(SEE_OTHER) {status(result2)}
assertResult(routes.AsisCtrl.getPreferences.url) {
await(result2).header.headers.get("Location").get
}
}
}
}
OatSpec is a trait the contains frequently used stuff from ScalaTest + Play.
GlobalWApp is the global settings object for this sub-module. withAuthenticator provides the needed information to authenticate the user "myUser"
I'm satisfied that it works correctly from the browser. I'd appreciate help in figuring out what's wrong with my test.
Thanks!
PS: Related question -- I have a form set up for the data input on this page. Haven't been able to figure out how to use it to avoid the hand-crafted Map in the call to withFormUrlEncodedBody. I think the call to withFormUrlEncodedBody is legitimate. The Play code appears to check headers first for a token with the correct name.
Related
I need to load test an API that requires an OAuth2.0 token via Gatling (of which I'm a complete novice!) but would like each virtual user to use the same token. I'm retrieving the token ok (I think) and putting it in a variable called 'access' but I keep getting 'no attribute named 'access' is defined' when the test itself starts.
My token retrieval looks like the following(along with httpConf, used below):
class MySimulation extends Simulation {
val httpConf = http
.baseUrl("https://MyBaseUrl.Com/")
.acceptHeader("application/json")
.doNotTrackHeader("1")
.acceptLanguageHeader("en-UK,en;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0")
.shareConnections
val header = Map("Content-Type" -> """application/x-www-form-urlencoded""")
al auth = scenario("Retrieve Token")
.exec(http("POST OAuth Req")
.post("https://SomeTokenUrl")
.formParam("resource", "someresource")
.formParam("grant_type", "somegranttype")
.formParam("client_secret", "someclientsecret")
.formParam("client_id", "someclientid")
.headers(header).check(status.is(200)).check(jsonPath("$.access_token").find.saveAs("access")))
I then tried setting up the load test as (Note: I did initially put 'Map', rather than the mutable variant, but read somewhere the default was immutable, and wondered if this was why the header couldn't update):
val headers_10 = scala.collection.mutable.Map("Content-Type" -> "application/json; charset=ISO-8859-1", "Authorization" -> "Bearer ${access}")
val scn = scenario("MyService Gatling test run")
.exec(http("")
.post("Myservice/api")
.headers(headers_10.toMap)
.body(StringBody("""{"SomeProperty": "Some Value"}"""))
.asJson
.check(status.is(200)))
setUp(
auth.inject(constantUsersPerSec(1) during (2 seconds)),
scn.inject(nothingFor(2 seconds),
constantUsersPerSec(10) during (10 seconds)
).protocols(httpConf))
.assertions(global.responseTime.max.lt(500))
.assertions(forAll.failedRequests.percent.lte(1))
.assertions(global.responseTime.mean.lte(100))
The idea was that the token retrieval would complete prior to the load test kicking in and the 'access' variable would then be used by the load test scenario, but it gives the following result:
ERROR : Failed to build request: No attribute named 'access' is defined
I've reached the end of my tether with it. I'm guessing it might be something to do with scopes, and perhaps the variable doesn't carry over to the load test scenario, but I've seen examples elsewhere that seem to recommend exactly that set up, so I don't know whether these other examples are partially complete or what.
Today I implemented this scenario for my project. Please see the code below and it will work for you as well.
Note: I have written this code with my API's required params. You can modify this code as per your requirement. For now, this code is written in a single class. I have implemented this code in a proper format as well with the use of different classes and property files. I will post that one as well.
For a single class, the code goes as follows:
`
package aapi
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class manifestSimulation extends Simulation {
private var token = ""
object authAvi{
// This is the request(API) which we are going to use for generating the auth token 1 time and then will feed this token into subsequent request.
var postBody = "{\"username\":\"devusername\”,\”password\”:\”devpassword”}”
val auth = scenario("Retrieve our auth Token which will be used in the subsequent request“)
.exec(
http("POST OAuth Req")
.post(“User Post URL“)
.body(StringBody(postBody))
.header("ClientId", “test”)
.header("DSN", “devDB”)
.header("accept", "application/json")
.header("Accept-Language", "en-us")
.header("Content-Type", "application/json")
.check(status.is(200))
.check(jsonPath("$.token")
.saveAs("token")))
.exitHereIfFailed
.exec{session => { token = session("token").as[String]
session}}
}
object manifest {
// This is the request(API) which we are going to hit multiple times using the token which we generated from the previous auth API
var manifestHeaders = Map("ClientId" -> “test”, "DSN" -> "devDB", "Token" -> "${token}")
val manifestMethod = exec(session => session.set("token", token))
.exec(http("Manifest Details")
.get(“Your get URL“)
.headers(manifestHeaders)
.check(status.is(200))
)
}
val scn = scenario(“**********This is your actual load test*******************”)
.exec(manifest.manifestMethod)
setUp(
authAvi.auth.inject(constantUsersPerSec(1) during (1 seconds)), // fire 1 requests per second for 1 second to retrieve token
scn.inject(nothingFor(4 seconds), // waits 4 seconds as a margin to process token and this time varies for every user
constantUsersPerSec(5) during (5 seconds))) // fire 5 requests per second for 5 seconds which will result in 25 (5*5) requests and overall 26 requests when the report gets generated (because we have 1 request for auth token and 25 requests of our intended API (25+1 = 26)
}`
Sessions are per user and no session data is shared between users. So while you have 1 user running your 'auth' scenario and saving the token, it is two different users that run 'scn' and they don't have access to the session values of the auth user.
It's not recommended practice, but you can solve this by pushing the auth token into a regular scala var and the setting this in the auth scenario and reading it in the main scenario - you just need to be sure that auth always completes before you inject any other users.
var token: String = ""
then in the auth scenario, have a step at the end such as
.exec(session => {
token = session("access").as[String]
session
})
then at the start of the scn scenario have a step to set the session variable
.exec(session.set("access", token))
I've used this pattern in the past and it works, but I'm sure there are nicer ways to do it
#Tarun,
When I did it, I had the 'exec' in my scenario, rather than the set up, and used the following syntax:
val dataToUse = feed(testData)
.exec(session => session.set("access", token))
.exec(http("")
.post("*the_URL_to_send_to)*")
.headers(headers_10.toMap)
.body(RawFileBody("${filePath}")).asJson
.check(status.is(200))
)
As mentioned in the comments in the previous discussion, this was because I was using a later version of gatling and the 'get' method was no longer part of the session api.
My full solution was as follows - note that are a number of things to look out for that might not apply to your solution. I used an object, as it just made things clearer in my mind for what I was trying to do! Also, some of the imports are probably redundant, as I included them as part of scattergun approach to finding something that worked!
Finally, I basically list the contents of a directory, and cycle through the files listed in it, using each one as a feeder. You look as if you're using a literal template of json, so probably don't need that, but I thought I would include it for completeness, as it's quite handy - if you change the format of your json, you don't need to mess around changing the template in the simulation, you just clear the directory and drop examples of the new format in there and away you go! :
package myTest
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
import scala.collection.JavaConversions._
import java.io.File
import java.io.FileNotFoundException
class myTestSimulation extends Simulation {
val httpConf = http
.baseUrl("*your_base_URL*")
.acceptHeader("application/json") // Here are the common headers
.doNotTrackHeader("1")
.acceptLanguageHeader("en-UK,en;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0")
.shareConnections
val header = Map("Content-Type" -> """application/x-www-form-urlencoded""");
private var token = ""
val auth = scenario("Retrieve Token")
.exec(
http("POST OAuth Req")
.post("*URL_for_Token*")
.formParam("resource", "*your_resource_value*")
.formParam("grant_type", "*your_grant_type*")
.formParam("client_secret", "*your_client_secret_value*")
.formParam("client_id", "*your_client_id_value*")
.headers(header)
.check(status.is(200)).check(jsonPath("$.access_token").find.saveAs("access")))
.exec{session => { token = session("access").as[String]
session}}
object myTestObject {
var headers_10 = scala.collection.mutable.Map("Content-Type" -> "application/json; charset=ISO-8859-1", "Authorization" -> "Bearer ${access}")
val testData = Iterator.continually(
new File("*pathway_to_file*") match {
case d if d.isDirectory => d.listFiles.map(f => Map("filePath" -> f.getPath))
case _ => throw new FileNotFoundException("Samples path must point to directory")
}).flatten
val myTestObjectMethod = feed(testData)
.exec(session => session.set("access", token))
.exec(http("")
.post("*the_URL_to_send_to(don't_forget_that_base_URL_above_is_automatically_stuck_to_the_front_of_this!)*")
.headers(headers_10.toMap)
.body(RawFileBody("${filePath}")).asJson
.check(status.is(200))
)
}
val scn = scenario("my_actual_load_test")
.exec(myTestSimulation.myTestObject)
setUp(
auth.inject(constantUsersPerSec(1) during (1 seconds)), // fire 1 requests per second for 1 second to retrieve token
scn.inject(nothingFor(2 seconds), // waits 2 seconds as a margin to process token
constantUsersPerSec(50) during (300 seconds) // fire 50 requests per second for 300 seconds
).protocols(httpConf))
.assertions(global.responseTime.max.lt(500)) // set max acceptable response time
.assertions(forAll.failedRequests.percent.lte(1)) // less than 1% of tests should fail
.assertions(global.responseTime.mean.lte(100)) // set average response time
}
I mean, I've probably made a typo somewhere along the line as I removed the sensitive stuff from it, but hopefully that will do what you need.
I'm something of a beginner with the Play Framework (2.5 and Scala in this case) - and I'm trying to learn by building a bot for Facebook messenger. However I've gotten stuck trying to verify the signature of the messages.
I've followed the Facebook documentation and created a webhook. Which handles POST requests usinggetRawMessages (see code below). This then tries to verify that the request is signed by Facebook using the verifyPayload function. However I can't seem to get the computed and the actual hashes to match up.
I've taken a lead from looking at this question: How to verify Instagram real-time API x-hub-signature in Java? which seems to be doing pretty much what I want, but for the Instagram equivalent. But I still can't seem to get it right.
val secret = "<facebooks secret token>"
def getRawMessages = Action (parse.raw) {
request =>
val xHubSignatureOption = request.headers.get("X-Hub-Signature")
try {
for {
signature <- xHubSignatureOption
rawBodyAsBytes <- request.body.asBytes()
} yield {
val rawBody = rawBodyAsBytes.toArray[Byte]
val incomingHash = signature.split("=").last
val verified = verifyPayload(rawBody, secret, incomingHash)
Logger.info(s"Was verified? $verified")
}
Ok("Test")
}
catch {
case _ => Ok("Test")
}
}
val HMAC_SHA1_ALGORITHM = "HmacSHA1"
def verifyPayload(payloadBytes: Array[Byte], secret: String, expected: String): Boolean = {
val secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA1_ALGORITHM)
val mac = Mac.getInstance(HMAC_SHA1_ALGORITHM)
mac.init(secretKeySpec)
val result = mac.doFinal(payloadBytes)
val computedHash = Hex.encodeHex(result).mkString
Logger.info(s"Computed hash: $computedHash")
computedHash == expected
}
The Facebook webhook docs state:
The HTTP request will contain an X-Hub-Signature header which contains
the SHA1 signature of the request payload, using the app secret as the
key, and prefixed with sha1=. Your callback endpoint can verify this
signature to validate the integrity and origin of the payload
Please note that the calculation is made on the escaped unicode
version of the payload, with lower case hex digits. If you just
calculate against the decoded bytes, you will end up with a different
signature. For example, the string äöå should be escaped to
\u00e4\u00f6\u00e5.
I'm guessing that what I'm missing is getting the payload properly escaped to unicode, but I can't seem to find a way to do it. And the answer in the referenced question also appeared to just get the byte array without doing anything more with it (jsonRawBytes = jsonRaw.asBytes();).
Any help with how to proceed would be much appreciated.
Turns out I was using the wrong secret all along. Should anyone else make the same mistake, please note that it "App Secret" that is available on the application dashboard that you want. See the screenshot below.
Looking for a good explanation on how to do authentication using akka HTTP. Given a route that looks like
val route =
path("account") {
authenticateBasic(realm = "some realm", myAuthenticator) { user =>
get {
encodeResponseWith(Deflate) {
complete {
//do something here
}
}
}
}
}
The documentation outlines a way, but then the pertinent part performing the actual authentication is omitted...
// backend entry points
def myAuthenticator: Authenticator[User] = ???
Where can I find an example implementation of such an authenticator? I have the logic already for authenticating a user given a user name and password, but what i can't figure out is how to get a username/password (or token containing both) from the HTTP request (or RequestContext).
Authenticator is just a function UserCredentials => Option[T], where UserCredentials in case of being (check with pattern matching) Provided have verifySecret(secret) method which you need to safely call and return Some (Some user for example) in case of success, like:
def myAuthenticator: Authenticator[User] = {
case p#Provided(username) =>
if(p.verifySecret(myGetSecret(username))) Some(username) else None
case Missing => None //you can throw an exeption here to get customized response otherwise it will be regular `CredentialsMissing` message
}
myGetSecret is your custom function which gets username and returns your secret (e.g. password), getting it possibly from database. verifySecret will securely compare (to avoid timing attack) provided password with your password from myGetSecret. Generally, "secret" is any hidden information (like hash of credentials or token) but in case of basic authentication it is just a plain password extracted from http headers.
If you need more customized approach - use authenticateOrRejectWithChallenge that gets HttpCredentials as an input, so you can extract provided password from there.
More info about authorization is in scaladocs.
Spray is hard!! I now know that my knowledge on HTTP protocol is not nearly enough and API design isn't easy. However, I still very much want my practice app to work. I'm writing this authentication for POST/PUT/DELETE method. It appears that there are at least two ways to do this: BasicAuth or write a custom directive.
I found this article:
BasicAuth: https://github.com/jacobus/s4/blob/master/src/main/scala/s4/rest/S4Service.scala
I'm trying it out because it looks simple.
The compile and run stages are fine, and the server runs. However, when I'm trying to send a PUT request to test the implementation (using Python's Httpie: http PUT 127.0.0.1:8080/sec-company/casper username=username token=123), the feedback is:HTTP/1.1 404 Not Found
Here's my route:
pathPrefix("sec-company") {
path("casper") {
//first examine username and token
authenticate(BasicAuth(CustomUserPassAuthenticator, "company-security")) {userProfile =>
post { ctx =>
entity(as[Company.Company]) { company =>
complete {
company
}
}
}
}
Here is my implementation of UserPassAuthenticator:
object CustomUserPassAuthenticator extends UserPassAuthenticator[UserProfile] {
def apply(userPass: Option[UserPass]) = Promise.successful(
userPass match {
case Some(UserPass(user, token)) => getUserProfile(user, token)
case _ => None
}
).future
}
First of all, is this the right way to implement authentication? Second, where does UserPassAuthenticator find the username and password?? Can I send back a better HTTP header other than 404 to indicate failed authentication?
If this is far from correct, is there any tutorial on authentication that I can follow? TypeSafe's Spray templates are more about overall patterns and less about Spray's functionality!
Thank you!
I had the same problem, even after looking at https://github.com/spray/spray/wiki/Authentication-Authorization (which says it's for an older version of Akka but it still seems to apply) I came up with the following:
trait Authenticator {
def basicUserAuthenticator(implicit ec: ExecutionContext): AuthMagnet[AuthInfo] = {
def validateUser(userPass: Option[UserPass]): Option[AuthInfo] = {
for {
p <- userPass
user <- Repository.apiUsers(p.user)
if user.passwordMatches(p.pass)
} yield AuthInfo(user)
}
def authenticator(userPass: Option[UserPass]): Future[Option[AuthInfo]] = Future { validateUser(userPass) }
BasicAuth(authenticator _, realm = "Private API")
}
}
I mix in this trait into the Actor that runs the routes and then I call it like this:
runRoute(
pathPrefix("api") {
authenticate(basicUserAuthenticator) { authInfo =>
path("private") {
get {
authorize(authInfo.hasPermission("get") {
// ... and so on and so forth
}
}
}
}
}
}
The AuthInfo object returned by the validateUser method is passed as a parameter to the closure given to the authorize method. Here it is:
case class AuthInfo(user: ApiUser) {
def hasPermission(permission: String) = user.hasPermission(permission)
}
In Spray (and HTTP), authentication (determining whether you have a valid user) is separate from authorization (determining whether the user has access to a resource). In the ApiUser class I also store the set of permissions the user has. This is a simplified version, my hasPermission method is a bit more complex since I also parametrize permissions, so it's not just that a particular user has permission to do a get on a resource, he might have permission to read only some parts of that resource. You might make things very simple (any logged-in user can access any resource) or extremely complex, depending on your needs.
As to your question, when using HTTP BASIC authentication (the BasicAuth object), the credentials are passed in the request in an Authorization: header. Your HTTP library should take care of generating that for you. According to the HTTP standard, the server should return a 401 status code if the authentication was incorrect or not provided, or a 403 status code if the authentication was correct but the user doesn't have permissions to view the content.
I have code like:
https://gist.github.com/daaatz/7665224
but dont know how to test request.
Trying mydomain/secured?user=John&password=p4ssw0rd etc but nothing works.
Can some one tell me or show example in js+html how to check is it working fine ?
Thanks
I've never used BasicAuth in Spray, so i'm not sure if this would be the complete answer, but i hope this will help you.
At first. in spray there is a great spray-testkit written on top of akka testkit. You should definitely check out SecurityDirectives test on github, this will show you how to test basic authentication. A little example, to make this simpler:
As for your route example, i would better edit to the following one:
val myRoute =
(path("secured") & get) {
authenticate(BasicAuth(myUserPassAuthenticator _, realm = "secure site")) {
userName => complete(s"The user is '$userName'")
}
}
}
Adding get directive will specify that this route expects a Get request and sealRoute is obsolete cause RejectionHandler and ExceptionHandler are provided implicitly with runRoute. It is used only in tests, if you want wo check exceptions/rejections.
Now in your tests you should construct auth entities, similar to the test one:
val challenge = `WWW-Authenticate`(HttpChallenge("Basic", "Realm"))
val doAuth = BasicAuth(UserPassAuthenticator[BasicUserContext] { userPassOption ⇒
Future.successful(Some(BasicUserContext(userPassOption.get.user)))
}, "Realm")
And you test case:
"simple auth test" in {
Get("security") ~> Authorization(BasicHttpCredentials("Alice", "")) ~> {
authenticate(doAuth) { echoComplete }
} ~> check { responseAs[String] === "BasicUserContext(Alice)" }
}
In Get("security") specifies that your test will send a Get request on "/security", then add Authorization to the test request, some action and the check part to test the request.
I've never tried to test BasicAuth, so there could be some mistakes.
I would look into CURL for testing routes in web applications, but I've also used the chrome extension Postman with great results as well.