Protection of sensitive technical information in API responses
import play.api.mvc._
import sangria.execution._
import sangria.parser.QueryParser
import sangria.schema.Schema
import play.api.libs.concurrent.Execution.Implicits.defaultContext
class ApplicationController extends Controller {
val schema: Schema[UserContext, Unit] = SchemaFactory.createSchema
def graphql: Action[AnyContent] = Action.async { implicit request =>
val query = request.body.asText.getOrElse("")
QueryParser.parse(query) match {
case Success(queryAst) =>
Executor.execute(schema, queryAst, new UserContext(new DAO))
.map(result => Ok(play.api.libs.json.Json.toJson(result)))
.recover {
case error: QueryAnalysisError => BadRequest(error.resolveError)
case error: ErrorWithResolver => InternalServerError(error.resolveError)
}
case Failure(error) => Future.successful(BadRequest(error.getMessage))
}
}
}
In the above code, the
graphql
function is exposed as a public endpoint in the
ApplicationController
. This function accepts any GraphQL query from the client and executes it against the application's GraphQL schema.
The vulnerability lies in the fact that the entire GraphQL schema (which includes both queries and mutations) is exposed to the client without any restrictions. This means that an attacker can send introspection queries to this endpoint and retrieve the entire structure of the API schema.
Introspection in GraphQL allows clients to know what operations (queries, mutations, and subscriptions) and types (objects, interfaces, unions, enums, scalars, and input types) are available in the schema. While this is a powerful feature that can be used for good (like generating API documentation or driving a GraphQL IDE), it can also be exploited by attackers to gather information about your API that they can use for further attacks.
In this case, an attacker can use the introspection feature to discover all the available queries and mutations in your API, including those that are not supposed to be publicly accessible. This can lead to information leakage and potential unauthorized actions if the exposed mutations are not properly protected.
import play.api.mvc._
import sangria.execution._
import sangria.parser.QueryParser
import sangria.schema.Schema
import play.api.libs.concurrent.Execution.Implicits.defaultContext
class ApplicationController extends Controller {
val schema: Schema[UserContext, Unit] = SchemaFactory.createSchema
def graphql: Action[AnyContent] = Action.async { implicit request =>
// Implement authentication and authorization
val authenticatedUser = authenticateUser(request)
if (authenticatedUser == null) {
Future.successful(Unauthorized("Unauthorized"))
} else {
val query = request.body.asText.getOrElse("")
QueryParser.parse(query) match {
case Success(queryAst) =>
// Implement access control
if (isUserAuthorized(authenticatedUser, queryAst)) {
Executor.execute(schema, queryAst, new UserContext(new DAO))
.map(result => Ok(play.api.libs.json.Json.toJson(result)))
.recover {
case error: QueryAnalysisError => BadRequest(error.resolveError)
case error: ErrorWithResolver => InternalServerError(error.resolveError)
}
} else {
Future.successful(Forbidden("Forbidden"))
}
case Failure(error) => Future.successful(BadRequest(error.getMessage))
}
}
}
// Dummy methods for demonstration purposes
def authenticateUser(request: Request[AnyContent]): User = {
// Implement user authentication
}
def isUserAuthorized(user: User, queryAst: Document): Boolean = {
// Implement access control
}
}
The updated code includes authentication and authorization mechanisms to ensure that only authorized users can access the GraphQL API.
The
authenticateUser
method is used to authenticate the user making the request. If the user is not authenticated, the server responds with an "Unauthorized" message.
The
isUserAuthorized
method is used to check if the authenticated user has the necessary permissions to perform the requested operation. If the user is not authorized, the server responds with a "Forbidden" message.
These changes help to prevent unauthorized access to the GraphQL API schema structure and reduce the risk of information leakage.
Please note that the
authenticateUser
and
isUserAuthorized
methods are placeholders for demonstration purposes. You should replace them with your own authentication and authorization logic.
In addition to these changes, you should also consider implementing rate limiting and throttling mechanisms, removing or obfuscating sensitive information from the GraphQL schema structure, regularly updating and patching the Play framework and other dependencies, performing regular security audits and penetration testing, and educating developers about secure coding practices.