A Five Minute Introduction to NoSQL Injection

Written by Pete Corey on Oct 24, 2016.

NoSQL injection is one of the most common vulnerabilities I find in Meteor applications.

I’m even starting to notice NoSQL injection vulnerabilities in other stacks and frameworks as well, like vanilla Node.js applications, and even Phoenix/Elixir applications.

I’ve written a short one page, five minute introduction to NoSQL injection for applications using MongoDB. My goal is to give you a quick primer on what the vulnerability looks like and how to fix it. That way, you’ll be able to identify it and seal up the vulnerability in your own applications.

I’m giving out a PDF version of the introduction to everyone who signs up for my newsletter. To sign up, enter your first name and email address below and hit subscribe!

Along with the NoSQL injection primer, you’ll receive weekly updates on all East5th articles and projects. Each newsletter also has a small selection of links and resources that I’ve found helpful when building secure, robust, maintainable software.

After you’ve signed up and read the NoSQL introduction, respond to the email and let me know if there’s any way I can help you build better software!

Phoenix Todos - Adding Lists and Tasks

This post is written as a set of Literate Commits. The goal of this style is to show you how this program came together from beginning to end.

Each commit in the project is represented by a section of the article. Click each section's header to see the commit on Github, or check out the repository and follow along.

Written by Pete Corey on Oct 19, 2016.

Creating Public Lists

Now that we’re loading and displaying all public lists, we’ll want to be able to create new lists.

To emulate our Meteor application, new lists will be given unique names, starting with "List A". If "List A" already exists, we’ll use "List B", and so on.

We’ll implement this functionality in a create function on our List model. When create is called with no arguments, we’ll default to using "List" as the base name, and "A" as the suffix:


def create, do: create("List", "A")

When create is called with two arguments (name, and suffix), we’ll find any lists that exist with the given name:


PhoenixTodos.List
|> findByName("#{name} #{suffix}")
|> Repo.all

If we find one or more lists with that name, we’ll increment our suffix to the next character and try again:


def handle_create_find(_, name, suffix) do
  [char] = to_char_list suffix
  create(name, to_string [char + 1])
end

If we don’t find a list with that name, we know that our name/suffix combination is unique, so we’ll insert the list.

We’ll call our create function whenever our "lists.public" channel receives a "create_list" message:


def handle_in("create_list", _, socket) do
  list = List.create

Once the list is added, we’ll want to broadcast this change to all connected clients, so they can add the new list to their UI:


broadcast! socket, "add_list", list

Finally, we’ll reply with the newly created list.

On the client, we removed all references to insert.call in favor of a newly created createList thunk. createList pushes the "create_list" message up to the server, and handles all responses.

web/channels/list_channel.ex

... + def handle_in("create_list", _, socket) do + list = List.create + |> Repo.preload(:todos) + + broadcast! socket, "add_list", list + + {:reply, {:ok, list}, socket} + end + end

web/models/list.ex

... + alias PhoenixTodos.Repo + @derive {Poison.Encoder, only: [ ... + def create(name, suffix) do + PhoenixTodos.List + |> findByName("#{name} #{suffix}") + |> Repo.all + |> handle_create_find(name, suffix) + end + def create, do: create("List", "A") + + def handle_create_find([], name, suffix) do + changeset(%PhoenixTodos.List{}, %{ + name: "#{name} #{suffix}", + incomplete_count: 0 + }) + |> Repo.insert! + end + + def handle_create_find(_, name, suffix) do + [char] = to_char_list suffix + create(name, to_string [char + 1]) + end + def public(query) do ... + def findByName(query, name) do + from list in query, + where: list.name == ^name + end + end

web/static/js/actions/index.js

... +export const CREATE_LIST_REQUEST = "CREATE_LIST_REQUEST"; +export const CREATE_LIST_SUCCESS = "CREATE_LIST_SUCCESS"; +export const CREATE_LIST_FAILURE = "CREATE_LIST_FAILURE"; + export function signUpRequest() { ... -export function joinListsChannel(channel) { +export function joinListsChannel(channelName) { return (dispatch, getState) => { ... - socket - .channel(channel) + let channel = socket.channel(channelName); + channel .join() ... dispatch(joinListsChannelSuccess(channel)); + dispatch(createAddListListeners(channel)); }) ... } + +export function createListRequest() { + return { type: CREATE_LIST_REQUEST }; +} + +export function createListSuccess() { + return { type: CREATE_LIST_SUCCESS }; +} + +export function createListFailure() { + return { type: CREATE_LIST_FAILURE }; +} + +export function createList(router) { + return (dispatch, getState) => { + const { channel } = getState(); + dispatch(createListRequest()); + channel.push("create_list") + .receive("ok", (list) => { + dispatch(createListSuccess()); + router.push(`/lists/${ list.id }`); + }) + .receive("error", () => dispatch(createListFailure())) + .receive("timeout", () => dispatch(createListFailure())); + } +} + +export function createAddListListeners(channel) { + return (dispatch, getState) => { + channel.on("add_list", list => { + dispatch(addList(list)); + }); + }; +}

web/static/js/components/ListList.jsx

... const { router } = this.context; - // const listId = insert.call((err) => { - // if (err) { - // router.push('/'); - // /* eslint-disable no-alert */ - // alert('Could not create list.'); - // } - // }); - // router.push(`/lists/${ listId }`); + this.props.createList(router); }

web/static/js/layouts/App.jsx

... import { connect } from "react-redux"; -import { signOut } from "../actions"; +import { signOut, createList } from "../actions"; ... <UserMenu user={user} logout={this.logout}/> - <ListList lists={lists}/> + <ListList lists={lists} createList={this.props.createList}/> </section> ... return dispatch(signOut(jwt)); + }, + createList: (router) => { + return dispatch(createList(router)); }

web/static/js/reducers/index.js

... ADD_LIST, + JOIN_LISTS_CHANNEL_SUCCESS, + CREATE_LIST_SUCCESS, } from "../actions"; ... socket: undefined, + channel: undefined, user: user ? JSON.parse(user) : user, ... return Object.assign({}, state, { socket: action.socket }); + case JOIN_LISTS_CHANNEL_SUCCESS: + return Object.assign({}, state, { channel: action.channel }); default:

List Ordering

Our last commit had a small issue. When lists were added, they appeared at the end of the list, as expected. However, when we reloaded the application, lists would appear in a seemingly random order.

To fix this, we need to order the lists by when they were inserted into the database.

A quick way to do this is to add an order_by clause to our List.public query:


order_by: list.inserted_at,

Now our lists will be consistently ordered, even through refreshes.

web/models/list.ex

... where: is_nil(list.user_id), + order_by: list.inserted_at, preload: [:todos]

Adding Tasks

Now that we can add lists, we should be able to add tasks to our lists.

We’ll start by adding an add_task function to our List module. add_task takes in the list’s id that we’re updating, and the text of the new task.

After we grab our list from the database, we can use Ecto.build_assoc to create a new Task associated with it:


Ecto.build_assoc(list, :todos, text: text)
|> Repo.insert!

Next, we’ll need to increment the list’s incomplete_count:


list
|> PhoenixTodos.List.changeset(%{
  incomplete_count: list.incomplete_count + 1
})
|> Repo.update!

Now we’ll wire our new add_task model function up to a "add_task" message handler on our ListChannel:


def handle_in("add_task", %{
  "list_id" => list_id,
  "text" => text
}, socket) do
  list = List.add_task(list_id, text)

Once we’ve added the list, we need to inform all subscribed clients that the list has been updated. We’ll do this by broadcasting a "update_list" message:


broadcast! socket, "update_list", list

Finally, we can replace our call to insert.call with a Redux thunk that triggers our "add_task" channel message:


this.props.addTask(this.props.list.id, input.value);

Now we can add new tasks to each of our todos!

web/channels/list_channel.ex

... + def handle_in("add_task", %{ + "list_id" => list_id, + "text" => text + }, socket) do + list = List.add_task(list_id, text) + |> Repo.preload(:todos) + + broadcast! socket, "update_list", list + + {:noreply, socket} + end + end

web/models/list.ex

... + def add_task(id, text) do + list = Repo.get(PhoenixTodos.List, id) + + Ecto.build_assoc(list, :todos, text: text) + |> Repo.insert! + + list + |> PhoenixTodos.List.changeset(%{ + incomplete_count: list.incomplete_count + 1 + }) + |> Repo.update! + end + def public(query) do

web/static/js/actions/index.js

... export const ADD_LIST = "ADD_LIST"; +export const UPDATE_LIST = "UPDATE_LIST"; ... +export const ADD_TASK_REQUEST = "ADD_TASK_REQUEST"; +export const ADD_TASK_SUCCESS = "ADD_TASK_SUCCESS"; +export const ADD_TASK_FAILURE = "ADD_TASK_FAILURE"; + export function signUpRequest() { ... +export function updateList(list) { + return { type: UPDATE_LIST, list }; +} + export function connectSocket(jwt) { ... +export function addTaskRequest() { + return { type: ADD_TASK_REQUEST }; +} + +export function addTaskSuccess() { + return { type: ADD_TASK_SUCCESS }; +} + +export function addTaskFailure() { + return { type: ADD_TASK_FAILURE }; +} + +export function addTask(list_id, text) { + return (dispatch, getState) => { + const { channel } = getState(); + dispatch(addTaskRequest()); + channel.push("add_task", { list_id, text }) + .receive("ok", (list) => { + dispatch(addTaskSuccess()); + }) + .receive("error", () => dispatch(addTaskFailure())) + .receive("timeout", () => dispatch(addTaskFailure())); + } +} + ... + channel.on("update_list", list => { + dispatch(updateList(list)); + }) };

web/static/js/components/ListHeader.jsx

... if (input.value.trim()) { - insert.call({ - listId: this.props.list._id, - text: input.value, - }, alert); + this.props.addTask(this.props.list.id, input.value); input.value = '';

web/static/js/pages/ListPage.jsx

... import Message from '../components/Message.jsx'; +import { connect } from "react-redux"; +import { addTask } from "../actions"; -export default class ListPage extends React.Component { +class ListPage extends React.Component { constructor(props) { ... <div className="page lists-show"> - <ListHeader list={list}/> + <ListHeader list={list} addTask={this.props.addTask}/> <div className="content-scrollable list-items"> ... }; + + +export default connect( + (state) => state, + (dispatch) => ({ + addTask: (list_id, text) => { + return dispatch(addTask(list_id, text)); + } + }) +)(ListPage);

web/static/js/reducers/index.js

... ADD_LIST, + UPDATE_LIST, JOIN_LISTS_CHANNEL_SUCCESS, ... }); + case UPDATE_LIST: + let lists = state.lists.map(list => { + return list.id === action.list.id ? action.list : list; + }); + return Object.assign({}, state, { lists }); case CONNECT_SOCKET:

Checking Tasks

Next up on our feature list is giving users the ability to toggle tasks as completed or incomplete.

To do this, we’ll create a set_checked_status helper method in our List model. Oddly, set_checked_status takes in a todo_id and a checked boolean. This will likely be a good place for a future refactor.

The set_checked_status function starts by grabbing the specified todo and it’s associated todo:


todo = Repo.get(PhoenixTodos.Todo, todo_id)
|> Repo.preload(:list)
list = todo.list

Next, it uses checked to determine if we’ll be incrementing or decrementing incomplete_count on our list:


inc = if (checked), do: - 1, else: 1

We can update our todo by setting the checked field:


todo
|> PhoenixTodos.Todo.changeset(%{
  checked: checked
})
|> Repo.update!

And we can update our list by setting the incomplete_count field:


list
|> PhoenixTodos.List.changeset(%{
  incomplete_count: list.incomplete_count + inc
})
|> Repo.update!

Now that we have a functional helper method in our model, we can call it whenever we receive a "set_checked_status" message in our list channel:


def handle_in("set_checked_status", %{
    "todo_id" => todo_id,
    "status" => status
  }, socket) do
  list = List.set_checked_status(todo_id, status)

Lastly, we’ll broadcast a "update_list" message to all connected clients so they can see this change in realtime.

Now we can replace our call to the old setCheckedStatus Meteor method with a call to an asynchronous action creator which pushes our "set_checked_status" message up to the server.

With that, our users can check and uncheck todos.

web/channels/list_channel.ex

... + def handle_in("set_checked_status", %{ + "todo_id" => todo_id, + "status" => status + }, socket) do + list = List.set_checked_status(todo_id, status) + |> Repo.preload(:todos) + + broadcast! socket, "update_list", list + + {:noreply, socket} + end + end

web/models/list.ex

... + def set_checked_status(todo_id, checked) do + todo = Repo.get(PhoenixTodos.Todo, todo_id) + |> Repo.preload(:list) + list = todo.list + inc = if (checked), do: - 1, else: 1 + + todo + |> PhoenixTodos.Todo.changeset(%{ + checked: checked + }) + |> Repo.update! + + list + |> PhoenixTodos.List.changeset(%{ + incomplete_count: list.incomplete_count + inc + }) + |> Repo.update! + end + def public(query) do

web/static/js/actions/index.js

... +export const SET_CHECKED_STATUS_REQUEST = "SET_CHECKED_STATUS_REQUEST"; +export const SET_CHECKED_STATUS_SUCCESS = "SET_CHECKED_STATUS_SUCCESS"; +export const SET_CHECKED_STATUS_FAILURE = "SET_CHECKED_STATUS_FAILURE"; + export function signUpRequest() { ... +export function setCheckedStatusRequest() { + return { type: SET_CHECKED_STATUS_REQUEST }; +} + +export function setCheckedStatusSuccess() { + return { type: SET_CHECKED_STATUS_SUCCESS }; +} + +export function setCheckedStatusFailure() { + return { type: SET_CHECKED_STATUS_FAILURE }; +} + + +export function setCheckedStatus(todo_id, status) { + return (dispatch, getState) => { + const { channel } = getState(); + dispatch(setCheckedStatusRequest()); + channel.push("set_checked_status", { todo_id, status }) + .receive("ok", (list) => { + dispatch(setCheckedStatusSuccess()); + }) + .receive("error", () => dispatch(setCheckedStatusFailure())) + .receive("timeout", () => dispatch(setCheckedStatusFailure())); + } +} + export function createAddListListeners(channel) {

web/static/js/components/TodoItem.jsx

... -/* import { - * setCheckedStatus, - * updateText, - * remove, - * } from '../../api/todos/methods.js';*/ - export default class TodoItem extends React.Component { ... setTodoCheckStatus(event) { - setCheckedStatus.call({ - todoId: this.props.todo.id, - newCheckedStatus: event.target.checked, - }); + this.props.setCheckedStatus(this.props.todo.id, event.target.checked); }

web/static/js/pages/ListPage.jsx

... import { connect } from "react-redux"; -import { addTask } from "../actions"; +import { + addTask, + setCheckedStatus +} from "../actions"; ... onEditingChange={this.onEditingChange} + setCheckedStatus={this.props.setCheckedStatus} /> ... return dispatch(addTask(list_id, text)); + }, + setCheckedStatus: (todo_id, status) => { + return dispatch(setCheckedStatus(todo_id, status)); }

Sorting Tasks

Just like our lists, our tasks are having a sorting problem. Checking and unchecking a task will randomize its position in the list.

There are two ways to solve this issue. We can either sort our todos when we Preload them on the server, or we can do our sorting on the client. For variety, let’s go with the second option.

Let’s sort primarily based on the “created at” timestamp of each task. To do this, we’ll need to serialize the inserted_at timestamp for each task we send to the client.


@derive {Poison.Encoder, only: [
  ...
  :inserted_at
]}

We can then sort our todos on this timestamp before rendering our <TodoItem> components:


.sort((a, b) => {
  return new Date(a.inserted_at) - new Date(b.inserted_at);
})

We’ll also want to have a secondary sort on the task’s text to break and ties that may occur (especially in seed data):


.sort((a, b) => {
  let diff = new Date(a.inserted_at) - new Date(b.inserted_at);
  return diff == 0 ? a.text > b.text : diff;
})

With those changes, our tasks order themselves correctly in each list.

web/models/todo.ex

... :text, - :checked + :checked, + :inserted_at ]}

web/static/js/pages/ListPage.jsx

... } else { - Todos = todos.map(todo => ( + Todos = todos + .sort((a, b) => { + let diff = new Date(a.inserted_at) - new Date(b.inserted_at); + return diff == 0 ? a.text > b.text : diff; + }) + .map(todo => ( <TodoItem

Final Thoughts

As we implement more and more functionality, we’re falling into a pattern. Phoenix channel events can be used just like we’d use Meteor methods, and we can manually broadcast events that act like Meteor publication messages.

Most of the work of implementing these features is happening on the front end of the application. The Redux boilerplate required to implement any feature is significant and time consuming.

Next week we should finish up the rest of the list/task functionality and then we can turn our attention to handling private lists.

Can Meteor Applications be "Mobile Only?"

Written by Pete Corey on Oct 17, 2016.

I recently finished up a security assessment with a team building a mobile-only application using Meteor.

One of the team’s goals was to prevent users from accessing the application through their browser. Their reasoning being that without access to the browser’s console, attackers would have a harder time exploiting any vulnerabilities that might exist in the application.

Interestingly, due to how Meteor applications work, truly removing the browser-facing portion of an application is impossible. Not only that, but removing the browser-facing user interface wouldn’t prevent a malicious user from exploring an application.

Uncovering the Bundle

We’ve previously talked about how a potential attacker (or security assessor) could extract a Meteor application’s server URL out of a compiled mobile application.

Armed with this information, the attacker can navigate to this URL, open up their browser console, and start poking your application.

But what if you wrapped your entire front-end in a Meteor.isCordova guard? Wouldn’t that prevent the attacker from being able to access the application with in their browser?

Wrapping your application in a Meteor.isCordova guard would initially prevent a potentially malicious user from seeing you’re application’s user interface. However, they would still be able to open their console and interact with your application’s Meteor object.

They can still inspect client-side methods, interact with Minimongo, call server-side methods, make subscriptions, etc…

Not only that, but a curious user could also view your application’s source and dive into your Javascript bundle. From there, they can peruse through all of the code used to render your user interface, paying special attention to method calls, subscriptions, etc…

On top of all of that, a highly motivated user could even redefine Meteor.isCordova to equal true when the application is initialized.

This would let the user view your user interface as if they were using a mobile device.

Ultimately, there is no way to prevent a motivated user from interacting with your application from their browser.

Final Thoughts

The battle for Meteor security (and all web application security) is fought on the server. Any client-side guards or precautions introduced into an application can easily be bypassed by a motivated user.

It’s important to remember that a user has complete control over their computer. This means that they have complete control over the portion of your application that runs on their computer. If the user tells your application to jump, it will ask how high. If the user says that Meteor.isCordova is true, then it’s true.

At the end of the day, the only real control you have over your application exists on the server. Take the time to secure your methods, publications, and server-side routes.