Too Much Sharing

Too Much Sharing

TMS - Too Much Serialization.

A hidden problem is common in web sites. Default serialization often delivers way more information than is required by the page it is sent to. Leading to sensitive information leakage.

Tony Abbott’s boarding pass.

A prime example was when Tony Abbott, former Australian Prime minister, innocently shared a picture of his boarding pass on social media. Something that is done to announce a vacation or business trip. An enterprising hacker used the boarding pass number to reveal what should have been private personal and business information from the booking web site.

See: https://mango.pdf.zone/finding-former-australian-prime-minister-tony-abbotts-passport-number-on-instagram

This was not an example of an IDOR, where information is accessed without authorization. The boarding pass number only served up data for that particular pass.  What was happening was Too Much Serialization (TMS) in the API request.

Nature of serialization.

Serialization happens most often in a web site when information from a database is transformed into JSON (JavaScript Object Notation) and sent over the network to the client web browser in response to a RESTful API call. JSON is used as this is naturally understood by the JavaScript language environment. Serialization can also be used internal to the webserver backend, but that is not so much of an issue as externally sent JSON.

Sending too much information can quickly occur when a model contains pertinent information and control or metadata used in the backend. Most serialization packages don’t make it easy to serve up different collections of data to different users. For example, an SQLAlchemy Python model may look like this:

 class User(db.Model):

    id = db.Column(db.Integer, primary_key=True)

    updated = db.Column(db.DateTime, default=datetime.utcnow)

    first_name = db.Column(db.String(264))

    last_name = db.Column(db.String(264))

    email = db.Column(db.String(120), index=True, unique=True)

    password_hash = db.Column(db.String(128))

    about_me = db.Column(db.String(140))

    failed_attempts = db.Column(db.Integer, default=0)

 

    # ----------------------------

    notes = db.relationship('UserNote',

                            foreign_keys='UserNote.user_id',

                            backref='user', lazy='dynamic')

    addresses = db.relationship('Address',

                                foreign_keys='Address.user_id',

                                backref='user', lazy='dynamic')

Clearly, first_name and last_name should be sent to the user, but notes and password_hash? A naïve serialization using Marshmallow will, by default, include every database field.  And by following the related links to notes, it will send back all the private company notes regarding this user. Possibly including personal comments from customer support. The password_hash field reveals what sort of password hashing system is in use. Opening it up to password cracking.

Just imagine the reputation damage if customers found out they were getting bad-mouthed behind closed doors!

Usually, the JavaScript code retrieving the serialization will only populate the web page with any data necessary for its display. Using browser developer tools, it is trivial to inspect the results of API calls and expose the unseen data.

Restricting access in the Model object by user role.

Fortunately, there is no requirement to restrict your Python serialization layer to Marshmallow. More convenient packages, such as Flask-Serialize, exist.

Let’s rewrite the User model to use Flask-Serialize

class User(FlaskSerializeMixin, db.Model):

    id = db.Column(db.Integer, primary_key=True)

    updated = db.Column(db.DateTime, default=datetime.utcnow)

    first_name = db.Column(db.String(264))

    last_name = db.Column(db.String(264))

    email = db.Column(db.String(120), index=True, unique=True)

    password_hash = db.Column(db.String(128))

    about_me = db.Column(db.String(140))

    failed_attempts = db.Column(db.Integer, default=0)



    # ----------------------------

    notes = db.relationship('UserNote',

                            foreign_keys='UserNote.user_id',

                            backref='user', lazy='dynamic')

    addresses = db.relationship('Address',

                                foreign_keys='Address.user_id',

                                backref='user', lazy='dynamic')

 


def fs_private_field(self, field_name):

    if not is_admin():

        if field_name in ['password_hash', 'notes', 'failed_attempts']:

            return True

    return False

By overriding the fs_private_field method, the model will not return internal or administration fields if the user is not an admin. Thus, protecting any route or API that serializes from this model object.

Conclusion

When serializing internal database and customer information to a user’s web browser, developers must be careful not inadvertently to expose confidential and sensitive information. It’s too easy to go on developing once a serialization layer is working without further consideration of what is being sent.

Using an intelligent serialization package such as Flask-Serialize, you can concentrate all these concerns into the model object. Allowing it to manage who sees what.

To view or add a comment, sign in

More articles by Andrew Rowe

Others also viewed

Explore content categories