Coded something up in Couch in an interesting way? Have a snippet or shortcode to share? Post it here for the community to benefit.
4 posts Page 1 of 1
While couchcms is as far as I can judge a pretty safe cms, it doesn't have any security measures in the manner of encryption for data-at-rest.

While developing a personal project I wrote a little script, which encrypts data that is sent in by databound forms on the front-end. Which wouldn't have been possible without the help of trendoman. So here my thanks to you.

The script uses a standard databound form in combination with a bit of extra php and phpseclib.
A working example can be found in the attachment. All code is commented.

You can use encrypted and plain data side by side with this, since it works like a standard databound form if you use the bound type and as the encrypted form if you use any other type than bound + the variable chain that encrypts it all.

It only works for front-end creation and editing though, if you edit any input in the admin-panel all data will be saved unencrypted.

encryptdataboundforms.php
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<?php include('Crypt/Twofish.php'); ?>
<!-- The phplibrary in use here is phpseclib 1.0.5. All documentation about it can be found on its website -->
<?php
global $key;
$key = "8bLuVY294GU1DXT4IC63GeZdMTc8mXqu"
?>
<!-- As you can see, the key is in the same file that is used for creating the data. While that is fine for example purposes, you should of course never do that in a production environment
the safe storage of ciphers is a whole topic for itself and there are better tutorials for it than this html comment, so I won't give you any big pointers except for, that the cipher should
be stored on the same server as the data -->

<cms:template clonable="1" hidden="1">
   <cms:editable type="text" label="Example Entry" name="example" />
   <cms:editable type="text" label="Example Sub" name="sub_example" />
</cms:template>

<!DOCTYPE html>

<html>
   <body>
      <!-- Since the text should be shown in plaintext when accessing it you have to decrypt it. First the base64 string has to be decoded into the encrypted string and then that
      has to be decrypted into plaintext. The extra step of encoding it in base64 is necessary so that couchcms can save the encrypted string to the db -->
      <h3>String in the database:</h3>
      <p>
         <cms:show example />
      </p>

      <h3>String after it has been decoded from base64:</h3>
      <p>
         <cms:php>
            echo base64_decode('<cms:show example />');
         </cms:php>
      </p>

      <h3>Plaintext after decryption:</h3>
      <p>
         <cms:php>
            $cipher = new Crypt_Twofish();
            $cipher->setKey($key);
            echo $cipher->decrypt(base64_decode('<cms:show example />'));
         </cms:php>
      </p>

      <cms:set submit_success="<cms:get_flash 'submit_success' />" />
      <cms:if submit_success >
          <h4>The data has been saved</h4>
      </cms:if>
   
      <cms:form
            masterpage=k_template_name
            mode='edit'
            page_id=k_page_id
            enctype='multipart/form-data'
            method='post'
            anchor='0'
            >

         <cms:if k_success >
            <!-- The entire encryption process happens here. The couchcms form variable is first encrypted with the help of phpseclib and then encoded in base64.
            That string is then changed back from a normal php variable into a couchcms variable.

            The second part below the encryption is the extra variable fpr sorting purposes which is just a substring of the original data without any encryption -->
            <cms:php>
               $cipher = new Crypt_Twofish();
               $cipher->setKey($key);

               $enc_example = base64_encode($cipher->encrypt('<cms:show frm_example />'));

               global $CTX;
               $CTX->set('db_example', $enc_example, 'global');

               $sub_vorname = substr('<cms:show frm_example />', 0, 3);
               $CTX->set('db_sub_example', $sub_example, 'global');

            </cms:php>

            <!-- In db-persist_form the encrypted couch variable is then finally saved into the database as the variable that was initially used in the form
            Since the entire editing and creation process is on the front-end (if you would edit the data in the admin panel it would be saved unencrypted)
            it didn't really matter what the title and name are. If you use those variables, that can of course be changed -->
            <cms:db_persist_form
                   _invalidate_cache='0'
                   k_page_name = "<cms:random_name />"
                   k_page_title = "<cms:random_name />"
                   example = db_example
            />

               <cms:if k_success >
                  <cms:set_flash name='success_msg' value='1' />
                  <cms:redirect k_page_link />
               </cms:if>

            </cms:if>

            <cms:if k_error >
               <div class="error">
                  <cms:each k_error >
                     <br><cms:show item />
                  </cms:each>
               </div>
         </cms:if>
         <!-- The nice thing about this databound form is, that you can use encrypted data and unencrypted data with just a slight change.
         If you want to encrypt the data, you have to use any other type than "bound" and of course write the entire variable chain to encrypt it.
         If you want unencrypted data, for example a picture or some data that is not of a personal nature, you can just use the standard "bound" input. -->

         <!-- The value of the input field has be decrypted beforehand for editing, otherwise the input is empty -->
         <p><label>Example</label></p>
         <p><cms:input name='example' type='text' value="<cms:php> $cipher = new Crypt_Twofish(); $cipher->setKey($key); echo $cipher->decrypt(base64_decode('<cms:show example />'));</cms:php>" /></p>

          <cms:if "<cms:not submit_success />" >
              <button type="submit">Speichern</button>
          </cms:if>
      </cms:form>
   </body>
</html>

<?php COUCH::invoke(); ?>


encryptdataboundforms-list.php
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<?php include('Crypt/Twofish.php'); ?>
<!-- The phplibrary in use here is phpseclib 1.0.5. All documentation about it can be found on its website -->
<?php
global $key;
$key = "8bLuVY294GU1DXT4IC63GeZdMTc8mXqu"
?>
<!-- As you can see, the key is in the same file that is used for creating the data. While that is fine for example purposes, you should of course never do that in a production environment
the safe storage of ciphers is a whole topic for itself and there are better tutorials for it than this html comment, so I won't give you any big pointers except for, that the cipher should
be stored on the same server as the data -->

<cms:template hidden="1" />

<!DOCTYPE html>

<html>
   <body>
   <!-- The list isn't sorted by the actual encrypted string, since after encoding it, it won't be the same as prior to encrypting it. As it should be. But that makes it useless for sorting
   it in alphabetical order for example. Because of that a second variable is created that contains the first three characters of the unencrypted string. The number of characters can of course
   be varied and if you don't need a list you can of course delete the entire second variable -->
      <cms:pages masterpage='encryptdataboundforms.php' orderby='sub_example' order='asc'>
            <a href="<cms:show k_page_link />">
               <!-- Since the text should be shown in plaintext when accessing it you have to decrypt it. First the base64 string has to be decoded into the encrypted string and then that
               has to be decrypted into plaintext. The extra step of encoding it in base64 is necessary so that couchcms can save the encrypted string to the db -->
               <cms:php>
                  $cipher = new Crypt_Twofish();
                  $cipher->setKey($key);
                  echo $cipher->decrypt(base64_decode('<cms:show example />'));
               </cms:php>
            </a>
      </cms:pages>

      <cms:set submit_success="<cms:get_flash 'submit_success' />" />
      <cms:if submit_success >
          <h4>The data has been saved</h4>
      </cms:if>
   
      <cms:form
          masterpage="encryptdataboundforms.php"
          mode='create'
          enctype='multipart/form-data'
          method='post'
          anchor='0'
          >

         <cms:if k_success >
            <!-- The entire encryption process happens here. The couchcms form variable is first encrypted with the help of phpseclib and then encoded in base64.
            That string is then changed back from a normal php variable into a couchcms variable.

            The second part below the encryption is the extra variable fpr sorting purposes which is just a substring of the original data without any encryption -->
            <cms:php>
               $cipher = new Crypt_Twofish();
               $cipher->setKey($key);

               $enc_example = base64_encode($cipher->encrypt('<cms:show frm_example />'));

               global $CTX;
               $CTX->set('db_example', $enc_example, 'global');

               $sub_vorname = substr('<cms:show frm_example />', 0, 3);
               $CTX->set('db_sub_example', $sub_example, 'global');

            </cms:php>

            <!-- In db-persist_form the encrypted couch variable is then finally saved into the database as the variable that was initially used in the form
            Since the entire editing and creation process is on the front-end (if you would edit the data in the admin panel it would be saved unencrypted)
            it didn't really matter what the title and name are. If you use those variables, that can of course be changed -->
            <cms:db_persist_form
                   _invalidate_cache='0'
                   k_page_name = "<cms:random_name />"
                   k_page_title = "<cms:random_name />"
                   example = db_example
            />

               <cms:if k_success >
                  <cms:set_flash name='success_msg' value='1' />
                  <cms:redirect k_page_link />
               </cms:if>

            </cms:if>

            <cms:if k_error >
               <div class="error">
                  <cms:each k_error >
                     <br><cms:show item />
                  </cms:each>
               </div>
         </cms:if>
         <!-- The nice thing about this databound form is, that you can use encrypted data and unencrypted data with just a slight change.
         If you want to encrypt the data, you have to use any other type than "bound" and of course write the entire variable chain to encrypt it.
         If you want unencrypted data, for example a picture or some data that is not of a personal nature, you can just use the standard "bound" input. -->
         <p><label>Example</label></p>
         <p><cms:input name='example' type='text' /></p>

          <cms:if "<cms:not submit_success />" >
              <button type="submit">Speichern</button>
          </cms:if>
      </cms:form>
   </body>
</html>

<?php COUCH::invoke(); ?>

Attachments

It's like to have 2 tags: <cms:encrypt /> and <cms:decrypt />. Can be made as addon or simply 2 functions put in kfunctions.php
Thanks for this post and congratulations on achievement!
@trendoman: Thanks for acknowledging it!

While working with my encryption script a bit more today, I noticed that the <cms:if> tag doesn't work anymore in combination with it. This is due to the base64 encoding, since it pads out the empty space. So you have to use the regular php if like that for example:


Code: Select all
<cms:php>
    $cipher = new Crypt_Twofish();
    $cipher->setKey($key);
    $example = $cipher->decrypt(base64_decode('<cms:show example />'));
    if (!empty($example)) {
        echo "$example";
    };
</cms:php>
Something very interesting happened: I was desperate looking for a way to store the data encrypted by phpseclib in a db; After hours and hours searching, I find this entry of CouchCMS (the CMS chosen in my projects, which by the way is excellent), I see your script and I find the correct way to apply base64_encode, I needed this to apply it to my project (in this case I'm not using CouchCMS)
I hope some time to be able to contribute with code and surely I will be making a donation.
Thanks to the whole CouchCMS community for everything !! :D :D :D
4 posts Page 1 of 1