Beefy Boxes and Bandwidth Generously Provided by pair Networks Joe
more useful options
 
PerlMonks  

Good IPC Message Protocols?

by pileofrogs (Priest)
on Jan 29, 2009 at 23:20 UTC ( #740068=perlquestion: print w/ replies, xml ) Need Help??
pileofrogs has asked for the wisdom of the Perl Monks concerning the following question:

Gracious Monks!

I'm writing some client-server code, and while I've got no problem setting up the pipes, sockets or what-have-you, I've realised I don't know much about formatting the conversation that goes on over those pipes or sockets or what-have-you.

To be more specific, I have a server process with privileged access and clients that pass instructions to the server. The idea being to separate the privileged and mundane happenings. To be even more specific, this is in reference to Security, root and CGI?.

So, once I've got my sockets set up, what's the most secure, by that I mean limited, constrained, way to format the messages between my two processes?

I'd like to assume that my non-privileged client has been completely hacked, so I want to make it as hard as possible for the baddies to crack open the privileged process. I want to pretend bad guys can send anything at all down the socket at my privileged process.

For example, I could do something vague, like var = value separated by newline, ala:

NAME=Pileofrogs PASSWORD=whatadorkiam ACTION=delete_account

I'd rather do something more secure. I thought I still use var and value pairs, but I'd encode the vars and values and delimit them by a character outside that encodings resulting character set. So, maybe I'd base64 encode the vars and values and separate them with a ':', using a newline to separate records, ala (totally fake):

mngaskjfh:alkjsdhf78ask4834598asdfsd 098234jkasdhfsaljks:lkhasdflg97698 asdkhlalkj86987:jkhasldhdflkjdfg87696

(note: I'm not encrypting. I'm trying to make it really hard for the privileged process to miss-interpret the message it receives.)

Then, when I parse it out I only have 67 (65 from base64 plus ':' and '\n') acceptable characters, and I can drop any messages containing bad characters on the floor. Since ':' and '\n' aren't members of the base64 output list, splitting my vars and values will be a snap.

Obviously, this only helps me through one step. I have to decode and then use those vars and values, but now at least, I know which are vars and which are values.

So! My questions are: Does this make sense? Is there a better way to do this?

Thanks!

--Pileofrogs

Comment on Good IPC Message Protocols?
Select or Download Code
Re: Good IPC Message Protocols?
by samtregar (Abbot) on Jan 30, 2009 at 01:22 UTC
    No, it doesn't make sense to me. Why would base64 encoding make it "really hard for the server to miss-interpret the message"? I'd encourage you to not invent your own data format - just re-use an existing one. XML or YAML would both work fine for this. If you use XML you can setup something like XML::Validator::Schema (plug!) to check that the client isn't sending you nonsense.

    -sam

Re: Good IPC Message Protocols?
by stvn (Monsignor) on Jan 30, 2009 at 02:11 UTC

    JSON makes an excellent format, and JSON-RPC has already figured out the RPC-ness of it. There is already a JSON::RPC::Common on cpan as well. One nice side effect of JSON is that it also makes it easy to web-ify things later since JSON is already spoken in the browser environment.

    -stvn
Re: Good IPC Message Protocols?
by oshalla (Deacon) on Jan 30, 2009 at 02:12 UTC
    I'd like to assume that my non-privileged client has been completely hacked, so I want to make it as hard as possible for the baddies to crack open the privileged process. I want to pretend bad guys can send anything at all down the socket at my privileged process.

    If the client has been completely hacked, what is to stop the bad person simply finding and copying whatever obfustication you are implementing ?

    As far as the framing of each message is concerned, I would:

    • have a message type field, so that you can change or improve things in future.

    • pack the elements together using: $data = pack((n/a*)*, @elements). It's up to you whether you treat those as name/attribute pairs...

    • prefix that packed data with its length and type, and append a 32-bit CRC (String::CRC32):

      $message = pack('n', $type) . $data ;
      $framed  = pack('N', length($message)) . $message . pack('N', crc32($message)) ;

    if the CRC passes, it's likely your message is intact, and you can unpack it. In the unlikely event of a CRC failure... I'd log it and close the connection. If you wanted to get fancy, you could have an error message type, and send one before closing...

    I guess you have some sort of password authentication when the client connection is established ? What I would do is as follows:

    • add a NONCE to the message:

      $message = pack('Nn', $nonce, $type) . $data ;

      where the NONCE is a random 32-bit number.

    • now for the authentication step I would send an authentication type message to the server, where the data portion is a TOKEN = MD5(password XOR NONCE).

      When the server responds to the authentication that message will also contain a NONCE. Both ends should compute and remember SEED = MD5(password XOR NONCE). The SEED is now a shared secret, based on the password.

    • for each message I would take MD5(SEED XOR NONCE) XOR that with the $data part of the message after having taken the 32-bit CRC. (Of course, this step must be omitted for authentication messages.)

      This is by no means a strong encryption ! But it will take a lot more than casual interest to decrypt a message, and each one is XOR'ed with a different "key". However, in order to create a fake message, you need the current session's SEED... which is hard. The SEED changes for each session, so replay attach is also hard. (To protect against replay attack during a session you could introduce a sequence number in each message -- starting from a random value at the start of each session.)

      Because the CRC is taken before the "encryption", the CRC will fail if the data is incorrectly encrypted.

    If the client is so completely hacked that a bad person can find the current SEED while a session is in progress... then you have a very serious problem. You could limit the damage by requiring periodic re-authentication -- which requires the password, which one assumes the bad person doesn't know. Beyond that... I don't know -- it looks like an almost impossible requirement (and I guess that requiring the password for every transaction would be ineffably tedious.)

      Although this is worthy advice, soundly presented ... nothing whatsoever at all to disagree with here ... the overwhelming thought for me is:  this has got to be something that has been done before. Secure IPC transport mechanisms already exist. As time passes, I find myself more and more reluctant to “implement” anything new if I can possibly, possibly avoid it.

      Vacuum CPAN first. Always.

Re: Good IPC Message Protocols?
by BrowserUk (Pope) on Jan 30, 2009 at 02:26 UTC
    Does this make sense?

    Not really. The only differences between the encoded version and the unencoded version are:

    • The characters you used as delimiters;

      '=' & "\n" versus ':' & "\n".

    • The character values you can transmit.

      With the encoded version being far more restrictive, without really making anything more secure. It could also come back to bite you if you suddenly have the need to deal with data elements that contain punctuation.

      Eg c:\... or firstname.lastname@someplace.com and many others.

    • The time you spend encoding and decoding.
    Is there a better way to do this?
    1. If you are looking to hide your stuff from the bad guys, use proper encryption.
    2. If you're looking for a reliable protocol, prefix your transmissions with a count value.

      This has many advantages. Being able to read a set number of bytes to get the count that tells you how many bytes to wait for on the next read can be a real boon to timelyness.

      Look at the n/a* pack template for one way to do that.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      I think you missed the part where the keys and values are base64'd as part of the serialization. Which actually increases the things that can be transmitted easily, and improves robustness because the payload can't be confused with the framing.

      So while I agree with the principle of not reinventing wheels, especially when security is on the line, pileofrogs isn't as misguided as all that.

        Now you point it out, you're right++.

        I got hung up on the "64 characters" bit and completely forgot that the input to the encoding process can contain any byte values.

        That said, I still much prefer length prefixing (via pack 'n/a*' etc.) which also allows the data packet to contain all byte values, but has the added advantages of:

        • 4(or 2 or 1)-bytes/per packet transmission overhead rather than 37%.
        • Negligible encoding/decoding overhead.
        • The ability to read for just what is required rather than rely on buffering and buffer management.

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Good IPC Message Protocols?
by papidave (Monk) on Jan 30, 2009 at 13:17 UTC
    One needs to consider relative risk when deciding what to remediate first. For a client-server app, I see four possible considerations:

    1. The client gets hacked
    2. Someone listens on your network
    3. Someone hacks the storage on your server
    4. Someone hacks the server app itself

    Risk 1 is minimized through strong authentication methods, and minimizing the amount of data that the client actually receives.

    Risk 2 is minimized by encrypting traffic between systems; see Crypt-SSLeay

    Risk 3 is minimized by encrypting the data store. I'm not enough of an expert to suggest a specific solution.

    Risk 4 might be minimized by using a compiled language, and including code that checksums the binary when it first runs.

    Personally, I think that the risk of 4 is so low, that everyone but government spy agencies can probably ignore it -- and they should be using a secure operating system that does that kind of thing automatically. For us mortals, risk #2 is the highest; if you are legitimately scared enough that you need to do more than #1 and #2, you probably shouldn't be using uncompiled Perl because of the risk that someone would read the source code and be able to reverse-engineer your data.

Re: Good IPC Message Protocols?
by sundialsvc4 (Monsignor) on Jan 30, 2009 at 13:52 UTC

    My general take is:   “dictum ne agas:   do not do a thing already done.”

    You need to be able to avail yourself of CPAN, all the way. JSON (or XML..SOAP) is a good, reliable way to freeze and thaw a message. Encryption (public or private key) is also readily available. What you really want here is “an encrypted transport layer,” presumably in the form of a Perl object, that provides all the rest of your application with reliable, authenticated message-passing while concealing all of the plumbing behind a nice sheetrock™ wall. I'm sure that this has been done before.

    Basically, “this is yet-another RPC requirement, nothing more.” Yeah, it's gotta be secure but “ hey, there's nothing new about that, either.” So you're looking at research-time, not coding time:   “finding the right shelf.”

Re: Good IPC Message Protocols?
by Cefu (Sexton) on Jan 30, 2009 at 19:26 UTC

    There is a term for what you are attempting:

    Security through Obscurity

    ...and it doesn't work. It's not even a halfway good idea; there is no partial payoff for partial effort. By making it difficult for a human to read you are simply setting up an interesting (or not-so-interesting) challenge for a would-be hacker. Without actual encryption, authentication, certification, etc. you gain absolutely no security from this approach.


    However the statements you made are a bit confusing:

    >I'd rather do something more secure.
    >(note: I'm not encrypting. I'm trying to make it really hard for the privileged process to miss-interpret the message it receives.)

    Perhaps security is not what you are after at all?

    If you are only trying to prevent malformed commands from causing your application to crash, there is a very simple rule: don't execute anything sent in the command. Define all actions the application can perform in the server side code and simply allow the remote commands to trigger those actions in safe, meaningful sequences. If you need data from the user, don't use it directly, translate it into something pre-defined and use that. Read about taint mode for more about how to not execute anything that came from that dirty/evil/hacked outside world. The basic concept of taint mode is "There is no five-second rule." If it has ever touched the outside world, wrap it in a napkin and throw it away.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://740068]
Approved by almut
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (7)
As of 2014-04-18 02:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (460 votes), past polls